Django REST update foreign key by id - django-rest-framework

Problem:
I want to update a foreign key field and I can not find my mistake why my changes to that fields are not saved. I want to be able to update clan and wingman.
serializer.py
class UpdateProfileInfoSerializer(serializers.Serializer):
id = serializers.CharField(required=True)
wingmanId = serializers.IntegerField(required=False)
clanId = serializers.IntegerField(required=False)
image = serializers.ImageField(required=False)
titleImage = serializers.ImageField(required=False)
description = serializers.CharField(required=False)
class Meta:
model = ProfileInfo
fields = ['id', 'clanId', 'description', 'image', 'titleImage', 'wingmanId']
def update(self, instance, validated_data):
clan_id = validated_data.get('clanId')
wingman_id = validated_data.get('wingmanId')
if clan_id:
if instance.clan:
instance.clan.id = clan_id
if wingman_id:
if instance.wingman:
instance.wingman.id = wingman_id
instance.image = validated_data.get('image', instance.image)
instance.titleImage = validated_data.get('titleImage', instance.titleImage)
instance.description = validated_data.get('description', instance.description)
instance.save()
return instance
Thank you for your help. I think I am doing something fundamentally wrong.

Try this please, I think it is better.
class UpdateProfileInfoSerializer(serializers.Serializer):
id = serializers.CharField(required=True)
wingmanId = serializers.IntegerField(required=False)
clanId = serializers.IntegerField(required=False)
image = serializers.ImageField(required=False)
titleImage = serializers.ImageField(required=False)
description = serializers.CharField(required=False)
class Meta:
model = ProfileInfo
fields = ['id', 'clanId', 'description', 'image', 'titleImage', 'wingmanId']
def update(self, instance, validated_data):
clan_id = validated_data.get('clanId')
wingman_id = validated_data.get('wingmanId')
if clan_id:
if instance.clan:
try:
clan_obj = Clan.objects.get(id=clan_id)
instance.clan = clan_obj
except:
pass
if wingman_id:
if instance.wingman:
try:
wingman_obj = Wingman.objects.get(id=wingman_id)
instance.wingman = wingman_obj
except:
pass
instance.image = validated_data.get('image', instance.image)
instance.titleImage = validated_data.get('titleImage', instance.titleImage)
instance.description = validated_data.get('description', instance.description)
instance.save()
return instance

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
]

create() got multiple values for keyword argument 'date_to_reserved'

class DateToReserveSerializer(serializers.Serializer):
date = serializers.CharField()
day = serializers.CharField()
time = serializers.CharField()
def __init__(self,date,time,day):
self.date = date
self.day= day
self.time = time
def getter(self):
return f'{self.date}/{self.day} in {self.time}'
class ReservingSerializer(serializers.ModelSerializer):
code_token = serializers.CharField(read_only=True)
date_to_reserved = serializers.CharField(write_only=True)
class Meta:
model = Reserve
fields = '__all__'
def create(self,validated_data):
request = self.context.get("request")
self.date_to_reserved = request.data['date_to_reserved']
DateToReserveSerializer.__init__(self,self.date_to_reserved[0],self.date_to_reserved[1],self.date_to_reserved[2])
try:
code = code_generator.random_code_generator()
return Reserve.objects.create(**validated_data,code_token=code,date_to_reserved=str(DateToReserveSerializer.getter(self)))
except IntegrityError:
if 'unique constraint':
return self.create(validated_data)
I tried to save some data into my database in DRF. I got an error! the date_to_reserved in my model is CharField....

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()

DRF getstream empty notifications

I'm trying to get a proof of concept of Django Rest Framework on getstream working to power inapp notifications.
My code https://github.com/morenoh149/django-rest-framework-getstream
models.py
class Snippet(models.Model, Activity):
created_at = models.DateTimeField(auto_now_add=True)
title = models.CharField(max_length=100, blank=True, default='')
code = models.TextField()
linenos = models.BooleanField(default=False)
language = models.CharField(
choices=LANGUAGE_CHOICES, default='python', max_length=100)
style = models.CharField(
choices=STYLE_CHOICES, default='friendly', max_length=100)
owner = models.ForeignKey(
'auth.User', related_name='snippets', on_delete=models.CASCADE)
highlighted = models.TextField()
#property
def activity_actor_attr(self):
return self.owner
#property
def activity_notify(self):
return [feed_manager.get_notification_feed(self.owner_id)]
class Meta:
ordering = ('created_at', )
def save(self, *args, **kwargs):
"""
Use the `pygments` library to create a highlighted HTML
representation of the code snippet.
"""
lexer = get_lexer_by_name(self.language)
linenos = self.linenos and 'table' or False
options = self.title and {'title': self.title} or {}
formatter = HtmlFormatter(
style=self.style, linenos=linenos, full=True, **options)
self.highlighted = highlight(self.code, lexer, formatter)
super(Snippet, self).save(*args, **kwargs)
serializers.py
from django.contrib.auth.models import User from rest_framework import serializers
from snippets.models import Snippet
class SnippetSerializer(serializers.HyperlinkedModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
highlight = serializers.HyperlinkedIdentityField(
view_name='snippet-highlight', format='html')
class Meta:
model = Snippet
fields = ('url', 'id', 'highlight', 'owner', 'title', 'code',
'linenos', 'language', 'style')
class ActivitySerializer(serializers.Serializer):
id = serializers.UUIDField()
foreign_id = serializers.CharField()
verb = serializers.CharField()
time = serializers.DateTimeField()
def __init__(self, *args, **kwargs):
object_serializer = kwargs.pop("object_serializer", None)
actor_serializer = kwargs.pop("actor_serializer", None)
super().__init__(self, *args, **kwargs)
if object_serializer:
self.fields["object"] = object_serializer()
else:
self.fields["object"] = serializers.CharField()
if actor_serializer:
self.fields["actor"] = actor_serializer()
else:
self.fields["actor"] = serializers.CharField()
class AggregatedSerializer(ActivitySerializer):
group = serializers.CharField()
activities = ActivitySerializer(many=True)
class NotificationSerializer(AggregatedSerializer):
is_seen = serializers.BooleanField()
is_read = serializers.BooleanField()
def get_activity_serializer(data, object_serializer=None, actor_serializer=None, **kwargs):
kwargs["object_serializer"] = object_serializer
kwargs["actor_serializer"] = actor_serializer
serializer = ActivitySerializer
if "is_seen" in data:
serializer = NotificationSerializer
elif "activities" in data:
serializer = AggregatedSerializer
return serializer(data, **kwargs)
views.py
class NotificationViewSet(viewsets.ViewSet):
"""
This viewset returns a notifications feed for the logged in user.
The feed contains events for when a relevant snippet is created.
"""
serializer_class = NotificationSerializer
def list(self, request):
feeds = feed_manager.get_news_feeds(self.request.user.id)
activities = feeds.get('timeline_aggregated').get()['results']
enriched_activities = enricher.enrich_aggregated_activities(activities)
serializer = get_activity_serializer(enriched_activities, SnippetSerializer, None, many=True)
return Response(serializer.data)
How do I get the /notifications/ endpoint to return the notifications for the signed in user?

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