How to get item name instead of item id in django restframework (foreignkey case) - django-rest-framework

I am trying to get itemwise inventory desired result is like this
[
{
'item' : shoes,
'total_unit' : 134
},
{
'item': sneaker,
'unit': 100
}
]
but I am getting result like
[
{
"item": 5,
"unit": 134
},
{
"item": 4,
"unit": 100
}
]
I want to get item name instead of id
Models.py
class Item(models.Model):
item_name = models.CharField(max_length=50)
srn_code = models.CharField(max_length=20,unique=True)
category = models.ForeignKey(Category,related_name='categories',on_delete=models.CASCADE)
def __str__(self):
return self.item_name
class Transaction(models.Model):
category = models.ForeignKey(Category,on_delete=models.CASCADE)
item = models.ForeignKey(Item,on_delete=models.CASCADE)
size = models.CharField(blank=True,max_length=20)
unit = models.IntegerField()
unit_price = models.DecimalField(decimal_places=2,max_digits=20)
supplier = models.ForeignKey(Supplier,on_delete=models.CASCADE)
tran_date = models.DateField(auto_now=False,auto_created=False,blank=False)
created_date = models.DateTimeField(auto_now=True)
def __str__(self):
return f"{self.item.item_name} {self.unit}"
serializer.py
class InventorySerializer(serializers.ModelSerializer):
item = serializers.CharField(read_only=True)
total_unit = serializers.IntegerField(read_only=True)
class Meta():
model = Transaction
fields = ['item','total_unit']
views.py
class InventoryModelViewSet(ModelViewSet):
queryset = Transaction.objects.all()
serializer_class = InventorySerializer
def get_queryset(self):
return Transaction.objects.values('item').annotate(
total_unit = Sum('unit')
).order_by('item')

Thanks for posting my question, I have sorted out my question, my solution is below, if anybody needs or gets query like this, then its might be helpful.
I have declared a method in serializers class for get item_name from another table
class InventorySerializer(serializers.ModelSerializer):
item = serializers.CharField(read_only=True)
total_unit = serializers.IntegerField(read_only=True)
item_name = serializers.SerializerMethodField()
class Meta():
model = Transaction
fields = ['item','item_name','total_unit']
def get_item_name(self, obj):
item_obj = Item.objects.filter(id=obj['item']).first()
item_name = model_to_dict(item_obj)
return item_name['item_name']
`

Related

Django TypeError get() argument after ** must be a mapping, not list

I'm creating a serializer for a model with a ManyToManyField (tag_id) that refers to a Tag table.
serializers.py
class CombinationSerializer(serializers.ModelSerializer):
# tag_id = serializers.PrimaryKeyRelatedField(queryset=Tag.objects.all(), source='tag', required=False, many=True)
tag_id = TagWithIdSerializer(many=True, required=False, write_only=False)
resource_id = serializers.PrimaryKeyRelatedField(queryset=Resource.objects.all(),
required=True,
source='resource',
write_only=False)
gameround_id = serializers.PrimaryKeyRelatedField(queryset=Gameround.objects.all(),
required=False,
source='gameround',
write_only=False)
user_id = serializers.PrimaryKeyRelatedField(queryset=CustomUser.objects.all(),
required=False,
source='user',
write_only=False)
class Meta:
model = Combination
depth = 1
fields = ('id', 'user_id', 'gameround_id', 'resource_id', 'tag_id', 'created', 'score')
def create(self, validated_data):
user = None
request = self.context.get("request")
if request and hasattr(request, "user"):
user = request.user
score = 0
combination = Combination(
user=user,
gameround=validated_data.get("gameround"),
resource=validated_data.get("resource"),
created=datetime.now(),
score=score
)
combination.save()
tag_data = validated_data.pop('tag_id', None)
if tag_data:
tag = Tag.objects.get_or_create(**tag_data)[0]
validated_data['tag_id'] = tag
combination.tag_id.add(validated_data.get("tag_id"))
return combination
This is the problematic code:
tag_data = validated_data.pop('tag_id', None)
if tag_data:
tag = Tag.objects.get_or_create(**tag_data)[0]
validated_data['tag_id'] = tag
combination.tag_id.add(validated_data.get("tag_id"))
This is the JSON object I am trying to send in Postman:
{
"gameround_id": 2015685170,
"resource_id": 327888,
"tag_id": [{"id": 2001372884, "name": "combination", "language": "en"}]
}
What am I doing wrong here? Can this be done any other way?
The tag_id maps on a list of dictionaries, not a dictionary. You thus can enumerate over it:
tag_data = validated_data.pop('tag_id', None)
for tag_item in tag_data:
tag = Tag.objects.get_or_create(**tag_item)[0]
validated_data['tag_id'] = tag
since these are lists, it might be better however to make a list of tags in the validated data:
tag_data = validated_data.pop('tag_id', None)
validated_data['tag_id'] = [
Tag.objects.get_or_create(**tag_item)[0]
for tag_item in tag_data
]

Django GraphQL Mutation Updated, but no change in Database

I created an update mutation as follows, with django==3.1.4 and graphene==2.1.8 :
# models.py
class CustomUser(AbstractUser):
# email = models.EmailField()
firebase_id = models.CharField(max_length=50, null=True)
nickname = models.CharField(max_length=50, null=True)
name = models.CharField(max_length=20, null=True)
gender = models.IntegerField(choices=Gender, default=3)
phone = models.CharField(max_length=20, null=True)
birthday = models.DateField(default=datetime(2020,1,1))
address = models.CharField(max_length=200, null=True)
profile_image = models.ImageField(default='default-avatar.png', upload_to='users/',
null=True, blank=True)
class UpdateMember(graphene.Mutation):
class Arguments:
firebase_id = graphene.String(required=True)
nickname = graphene.String()
name = graphene.String()
gender = graphene.Int()
phone = graphene.String()
birthday = graphene.Date()
address = graphene.String()
profile_image = graphene.String()
class Meta:
exclude = ["password"]
member = graphene.Field(MemberType)
success = graphene.Boolean()
# #login_required
#staticmethod
def mutate(root, info, firebase_id, **kwargs):
success = False
member_instance = CustomUser.objects.get(firebase_id=firebase_id)
if member_instance:
print(member_instance)
success = True
for k, v in kwargs.items():
member_instance.k = v
member_instance.save()
return UpdateMember(member=member_instance, success=True)
else:
return UpdateMember(member=None, success=False)
Running GQL below:
mutation {
updateMember(
firebaseId:"777",
name:"JJJJ")
{
success
}
}
Response:
{
"data": {
"updateMember": {
"success": true
}
}
}
But I checked the database, it seems no change in it, I think .save() should have done the work persisting changes to database......
Creating Member works fine. Using PostgresQL
Could anyone figure out why?
There is several issues in your code:
You can not assign your model fields using string like that. See this thread
for k, v in kwargs.items():
member_instance.k = v
member_instance.save()
Currently your member_instance.k has nothing to do with variable k inside for loop.
firebase_id field should be unique.
Currently you call CustomUser.objects.get(firebase_id=firebase_id) which is risky because firebase_id is not unique field. This may lead Multiple objects error if you have more than one CustomUsers saved with same id. To fix it, just define:
class CustomUser(AbstractUser):
# email = models.EmailField()
firebase_id = models.CharField(max_length=50, unique=True)
...
To check if your member_instance has really updated. You can for example print out the values before saving it and run some test cases before final implementation. For example:
if member_instance:
print(member_instance)
success = True
for k, v in kwargs.items():
member_instance.k = v
print(member_instance.k)
print(k)
print(getattr(member_instance, k))
member_instance.save()

Graphene-Django create multiple instances

Let's say I've got a models.py with two tables:
class Category(models.Model):
cat = models.CharField(max_length=100)
class Thing(models.Model):
desc = models.CharField(max_length=100)
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, blank=True)
and my schemas as follows:
class ThingType(DjangoObjectType):
class Meta:
model = Thing
class FindThing(graphene.ObjectType):
things = graphene.List(
ThingType,
search=graphene.String(),
thing=graphene.ID(),
)
def resolve_things(self, info, thing=None, search=None, **kwargs):
qs = Thing.objects.all()
if search:
filter = (
Q(desc__icontains=search)
)
qs = qs.filter(filter)
if thing:
qs = qs.filter(id=thing)
return qs
class CreateThing(graphene.Mutation):
id = graphene.Int()
desc = graphene.String()
category = graphene.Field(FindCategory)
class Arguments:
desc = graphene.String()
category = graphene.Int()
def mutate(self, info, desc, category):
thing = Thing(
desc=desc,
category=Category.objects.get(id=category)
)
thing.save()
return CreateThing(
id=thing.id,
desc=thing.desc,
category=thing.category_id
)
class CategoryType(DjangoObjectType):
class Meta:
model = Category
class GetCategory(graphene.ObjectType):
category = graphene.List(
CategoryType,
category=graphene.String(),
)
def resolve_category(self, info, category=None, **kwargs):
qs = Category.objects.all()
if category:
get = (
Q(category__contains=category)
)
qs = qs.get(get)
return qs
class FindCategory(graphene.ObjectType):
categories = graphene.List(
CategoryType,
search=graphene.String(),
cat=graphene.ID(),
)
def resolve_categories(self, info, cat=None, search=None, **kwargs):
qs = Category.objects.all()
if search:
filter = (
Q(cat__icontains=search)
)
qs = qs.filter(filter)
if cat:
qs = qs.filter(id=cat)
return qs
class CreateCategory(graphene.Mutation):
id = graphene.Int()
cat = graphene.String()
desc = graphene.String()
class Arguments:
cat = graphene.String()
desc = graphene.String()
def mutate(self, info, cat, desc):
category = Category(
cat=cat
)
category.save()
thing = Thing(
desc=desc,
category_id=category.id
)
thing.save()
return CreateCategory(
id=category.id,
cat=category.cat,
desc=thing.desc,
)
I've managed to create a schema where one can create a new category that already links to a newly created single thing:
mutation createCategory{
createCategory(cat:"cat7", desc:"defg"){
id
cat
desc
}
}
Is it possible to create a CreateCategory django-graphene schema where one can create a category with multiple additional new things?
You could allow the CreateCategory mutation to accept a list of descriptions for multiple things:
CreateCategory:
descs = graphene.List(String)
class Arguments:
descs = graphene.List(String)
and then loop over this list inside the mutate function:
new_things = []
for desc in descs:
thing = Thing(
desc=desc,
category_id=category.id
)
thing.save()
new_things.append(thing.desc)
return CreateCategory(
id=category.id,
cat=category.cat,
descs=new_things
)

Django Rest Framework - HyperlinkedRelatedField for abstract base class

My question is if is possible to add an HyperlinkedRelatedField in a serializer to get only the attributes of the base class. for example:
I want a json like that:
{
"modules": [
{
"moduleName": "M1: Fundamentos Técnicos",
"moduleDetails": "Bla bla bla.",
"moduleID": 0,
"userScore": 3,
"slides": [
{
"slideType": "Content",
"slideID": 0
},
{
"slideType": "Minigame1",
"slideID": 1
},
{
"slideType": "Video",
"slideID": 6
}
]
}
]
}
Here, Slide is the base class and Video and Minigame are subclasses.
Is possible this?, is possible add a HyperlinkedRelatedField for the url of each slide?.
Thanks in advance!
UPDATE!
this are my models:
class Module(TimeStampedModel):
moduleID = models.AutoField(primary_key=True)
moduleName = models.CharField(
max_length=100,
verbose_name='Nombre del modulo')
moduleDetails = models.TextField(verbose_name='Detalle')
moduleBullet1 = models.CharField(max_length=100, verbose_name='Punto 1')
moduleBullet2 = models.CharField(max_length=100, verbose_name='Punto 2')
moduleBullet3 = models.CharField(max_length=100, verbose_name='Punto 3')
moduleImageURL = models.ImageField(
upload_to="modulos", verbose_name='Imagen')
userScore = models.PositiveSmallIntegerField(
default=0, verbose_name='Score de usuario')
class Slide(TimeStampedModel):
CONTENT = 'Content'
MINIGAME = 'Minigame'
VIDEO = 'Video'
SLIDE_TYPE_CHOICES = (
(CONTENT, 'Contenido'),
(MINIGAME, 'Minigame'),
(VIDEO, 'Video'),
)
slideType = models.CharField(
max_length=20,
choices=SLIDE_TYPE_CHOICES,
default=CONTENT,
)
slideID = models.AutoField(primary_key=True)
slideOrder = models.PositiveSmallIntegerField(
verbose_name='Orden de visualizacion')
module = models.ForeignKey(Module, on_delete=models.CASCADE,
related_name="%(app_label)s_%(class)s_related",
related_query_name="%(app_label)s_%(class)ss",)
class Meta:
abstract = True
class VideoContent(Slide):
videoURL = models.URLField(verbose_name='URL de video')
class Minigame(Slide):
timeToFail=models.PositiveSmallIntegerField()
And in my serializers.py i want something like this:
class SlideSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Slide
fields = ('url', 'slideType', 'slideID')
class ModuleSerializer(serializers.ModelSerializer):
slides = SlideSerializer(many=True, read_only=True)
class Meta:
model = Module
fields = ('moduleID', 'moduleName', 'moduleDetails', 'moduleImageURL', 'userScore', 'slides')
My view.py
class ModuleViewSet(viewsets.ModelViewSet):
"""
This viewset automatically provides `list`, `create`, `retrieve`,
`update` and `destroy` actions.
"""
queryset = Module.objects.all()
serializer_class = ModuleSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
class SlideViewSet(viewsets.ModelViewSet):
"""
This viewset automatically provides `list`, `create`, `retrieve`,
`update` and `destroy` actions.
"""
queryset = Slide.objects.all()
serializer_class = SlideSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
Slide.Objects raise an error, AttributeError: type object 'Slide' has no attribute 'objects'
You can't query abstract base classes. Slice is an abstract class, you cannot use queryset = Slide.objects.all() in SlideViewSet because an abstract model isn't an actual database object, and therefore cannot be queried.
You have to choose between abstract inheritance, in which case there is no database relationship between the two classes, or multi-table inheritance, which keeps the database relationship at a cost of efficiency (an extra database join) for each query.
Using multi-table inheritance
class Slide(TimeStampedModel):
CONTENT = 'Content'
MINIGAME = 'Minigame'
VIDEO = 'Video'
SLIDE_TYPE_CHOICES = (
(CONTENT, 'Contenido'),
(MINIGAME, 'Minigame'),
(VIDEO, 'Video'),
)
slideType = models.CharField(
max_length=20,
choices=SLIDE_TYPE_CHOICES,
default=CONTENT,
)
slideID = models.AutoField(primary_key=True)
slideOrder = models.PositiveSmallIntegerField(
verbose_name='Orden de visualizacion')
module = models.ForeignKey(Module, on_delete=models.CASCADE,
related_name="%(app_label)s_%(class)s_related",
related_query_name="%(app_label)s_%(class)ss",)
class VideoContent(Slide):
videoURL = models.URLField(verbose_name='URL de video')
class Minigame(Slide):
timeToFail=models.PositiveSmallIntegerField()
Now, your viewset:
class SlideViewSet(viewsets.ModelViewSet):
"""
This viewset automatically provides `list`, `create`, `retrieve`,
`update` and `destroy` actions.
"""
queryset = Slide.objects.all()
serializer_class = SlideSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
Documentation on multi-table inheritance:
multi-table inheritance uses an implicit OneToOneField to link the
child and the parent, it’s possible to move from the parent down to
the child
Hope this clears the things up.

Django get_initial not working

Django get_initial is not populating product field in the form. I am expecting a drop down, with the queryset results as defined in the get_initial overridden function.
class PurchaseRequestDetailForm(forms.ModelForm):
class Meta:
model = PurchaseRequestDetail
fields = ["product", "variations", "quantity", "fulfilled", "vat", "discount", "surcharges", "active"]
exclude = ("purchase_request", )
class PurchaseRequestDetailCreateView(CreateView):
model = PurchaseRequestDetail
form_class = PurchaseRequestDetailForm
template_name = "inventory/purchaserequestdetail_form.html"
def get_pr_obj(self):
pr_id = self.request.session["pr_id"]
return PurchaseRequest.objects.get(id=pr_id)
def get_initial(self):
initial = super(PurchaseRequestDetailCreateView, self).get_initial()
try:
pr_obj = self.get_pr_obj()
initial["product"] = pr_obj.vendor.vendors_products.all()
except KeyError:
pass
self.form_class(initial)
return initial
template:
<td>{{ form.product|css_class:"form-control" }}</td>
An easy way to set a ModelChoiceField queryset is to set the field attribute in the form init();
class PurchaseRequestDetailForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
# Get initial data passed from the view
self.product = None
if 'product' in kwargs['initial']:
self.product = kwargs['initial'].pop('product')
super(PurchaseRequestDetailForm, self).__init__(*args, **kwargs)
self.fields['product'].queryset = self.product
class Meta:
model = PurchaseRequestDetail
fields = ["product", "variations", "quantity", "fulfilled", "vat", "discount", "surcharges", "active"]
exclude = ("purchase_request", )
You should hook in to get_form_kwargs from ModelFormMixin to pass your data to the form.
class PurchaseRequestDetailCreateView(CreateView):
model = PurchaseRequestDetail
form_class = PurchaseRequestDetailForm
template_name = "inventory/purchaserequestdetail_form.html"
def get_pr_obj(self):
pr_id = self.request.session["pr_id"]
return PurchaseRequest.objects.get(id=pr_id)
def get_form_kwargs(self):
"""
Returns the keyword arguments for instantiating the form.
"""
kwargs = super(PurchaseRequestDetailCreateView, self).get_form_kwargs()
kwargs.update(
{'initial':
{'product': pr_obj.vendor.vendors_products.all()}
}
)
return kwargs

Resources