I'm trying to map a foreign key to POST data when creating a new object through a serializer. There are two foreign keys in the object, one is serializing perfectly, the other is creating an error.
Model:
class Event(models.Model):
owner = models.ForeignKey('auth.User', related_name='owner', blank=True)
date = models.DateField('eventdate')
time = models.TimeField('eventtime', default=now)
eventtype = models.ForeignKey(EventType, related_name='eventtype', blank=True)
# duration = models.DurationField()
location = models.CharField(max_length=200, blank=True)
attenders = models.ManyToManyField(User, related_name='attenders')
invited = models.ManyToManyField(User, related_name='invitedlist')
View:
class EventMixin(RetrieveUpdateDestroyAPIView, CreateAPIView):
serializer_class = EventSerializer
def get_queryset(self):
return Event.objects.all()
def partial_update(self, request, *args, **kwargs):
request['owner'] = request.user
sname = request['eventtype']
request['eventtype'] = EventType.objects.filter(sname=sname)
json_str = json.dumps(self.request.data)
data = json.loads(json_str)
try:
invited = list(data['toInvite'])
for i in invited:
for j in User.objects.filter(username=i):
invite = EventInvite(invited=j, sender=request.user, event=self.get_object())
invite.save()
self.get_object().invited.add()
except KeyError:
pass
return super(EventMixin, self).partial_update(request, *args, **kwargs)
def create(self, request, *args, **kwargs):
new = {}
new['owner'] = request.user.__dict__
new['date'] = request.data['date']
new['time'] = request.data['time']
new['location'] = request.data['location']
sname = request.data['eventtype']
new['eventtype'] = EventType.objects.get(sname=sname).__dict__
json_str = json.dumps(self.request.data)
data = json.loads(json_str)
serializer = EventMixinSerializer(data=new)
with open('/tmp/log.txt', 'w+') as f:
f.write(str(serializer.is_valid()))
f.write(str(serializer.validated_data))
f.close()
serializer.is_valid();
serializer.save()
try:
invited = list(data['toInvite'])
for i in invited:
for j in User.objects.filter(username=i):
invite = EventInvite(invited=j, sender=request.user, event=self.get_object())
invite.save()
self.get_object().invited.add()
except KeyError:
pass
Serializer:
class EventMixinSerializer(serializers.ModelSerializer):
owner = UserSerializer(read_only=True)
eventtype = EventTypeSerializer()
attenders = FriendsListingField(read_only=True)
invited = FriendsListingField(read_only=True)
class Meta:
model = Event
fields = ('owner', 'eventtype', 'date', 'time', 'location', 'id', 'attenders', 'invited')
def create(self, validated_data):
owner = validated_data.pop('owner')
owner = owner.instance
eventtype = validated_data.pop('eventtype')
eventtype = eventtype.instance
event = Event.objects.create(owner=owner, eventtype=eventtype, **validated_data)
event.save()
return event
Error when owner field present:
False
{'owner': OrderedDict([('username', ['A user with that username already exists.'])])}
Result when UserSerializer(read_only=True) (pretty much diabling it):
True
OrderedDict([('eventtype', OrderedDict([('lname', 'Swimming'), ('sname', 'SWM'), ('category', '1')])), ('date', datetime.date(2015, 12, 22)), ('$
(Notice the event type data in the result)
Thanks!
You need to remove the validators from UserSerializer.
Assuming UserSerializer is a User ModelSerializer it'll extract the unique constraint on the User.username from the Model and your validation will fail.
To work this around you'll need to remove the UniqueValidator by overriding the validators list for the username field of the UserSerializer
Related
I have 2 serializers serializer_1 and serializer_2 which are both model serilizer i want to execute update method of serializer_1 from create method of serializer_2 how can i achieve that?
class serializer_1(serializers.ModelSerializer):
date = serializers.DateTimeField(required=False, allow_null=True)
ispublic = serializers.BooleanField(allow_null=False)
details_api_url = serializers.SerializerMethodField()
dispute_types = OtherSerializer(many=True, required=False, write_only=True)
nature_of_dispute_list = serializers.SerializerMethodField()
plaintiff = OtherSerializer(many=True, required=False, write_only=True)
defendant = OtherSerializer(many=True, required=False, write_only=True)
claims_rep = OtherSerializer(many=True, required=False, write_only=True)
class Meta:
model = Media
fields = "__all_"
def update(self, instance, validated_data):
date = validated_data.pop('close_out_date', None)
plaintiff_data = validated_data.pop('plaintiff', [])
defendant_data = validated_data.pop('defendant', [])
claims_rep_data = validated_data.pop('claims', [])
is_summary_public_previous = instance.is_summary_public
obj = super().update(instance, validated_data)
return obj
class serializer_2(serializers.ModelsSerializer):
class Meta:
model = Fedia
fields = "__all__"
def create(self, validated_data):
request = self.context['request']
**serilizer_1_data** = validated_data.pop('serialzer_1_data', None)
is_final = validated_data.get('is_final')
serializer_1_object = Media.objects.create(**serializer_1_data)
if is_final:
**Call Serializer_1 Update method**
I have access to date,plaintiff etc mentioned under serializer_1 in create method of serilizer_2 through serilizer_1_data
I need to bulk update ("is_read" = True) Message instanses by given list of ids in one request with this code:
{"ids": [11, 4, 7]}
Model:
class Message(models.Model):
text = models.TextField(max_length=500, verbose_name=_("Text"))
sender = models.ForeignKey(
to=User,
on_delete=models.CASCADE,
related_name="sender_message",
verbose_name=_("User"),
)
thread = models.ForeignKey(
to="Thread",
on_delete=models.CASCADE,
related_name="thread_message",
verbose_name=_("Thread"),
)
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created"))
updated_at = models.DateTimeField(auto_now=True, verbose_name=_("Updated"))
is_read = models.BooleanField(default=False, verbose_name=_("Is read"))
I have this serializer:
class MessageIsReadSerializer(serializers.ModelSerializer):
class Meta:
model = Message
fields = ("id", "text", "sender", "is_read")
And method in views.py:
class MessageIsRead(APIView):
permission_classes = (AllowAny,)
queryset = Message.objects.all()
def put(self, request, *args, **kwargs):
id_list = request.data['ids']
instances = []
for item in id_list:
obj = self.queryset.filter(id=item)
obj.is_read = True
instances.append(obj)
serializer = MessageIsReadSerializer(instances, many=True)
return Response(serializer.data)
urls.py
urlpatterns = [
path("messages-read/", MessageIsRead.as_view()),
]
But as a result of running this query I get an error:
AttributeError at /messages-read/
Got AttributeError when attempting to get a value for field `text` on serializer
`MessageIsReadSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the
`QuerySet` instance.
Original exception text was: 'QuerySet' object has no attribute 'text'.
What is wrong?
With help of Bartosz Stasiak I've fixed my verion of put method.
def put(self, request, *args, **kwargs):
id_list = request.data['ids']
instances = []
for item in id_list:
obj = self.queryset.get(id=item)
obj.is_read = True
obj.save()
instances.append(obj)
serializer = MessageIsReadSerializer(instances, many=True)
return Response(serializer.data)
First: here you are getting a queryset, not an instance, so later in your code you are appending querysets to the instances list. If you want to access single instance you should use get instead of filter
single_instance = self.queryset.get(id=item)
If you want to update multiple items you can use:
def put(self, request, *args, **kwargs):
id_list = request.data['ids']
instances = self.queryset.filter(id__in=id_list)
instances.update(is_read=True)
serializer = MessageIsReadSerializer(instances, many=True)
return Response(serializer.data)
Upon trying to save a Model instance as a result of saving a ModelForm, I'm getting the following error:
ValueError: "<Question: NameError: 'x' is not defined>" needs to have
a value for field "id" before this many-to-many relationship can be used.
The question instance is being created to an extent, yet the primary key is not being generated. What needs to be fixed in order for the primary key to be attached so it can be stored in the database?
class PostQuestionPage(QuestionPage):
template_name="questions/create_question.html"
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['question_form'] = QuestionForm(self.request.POST or None)
con
return context
def get(self, request):
context = self.get_context_data()
return self.render_to_response(context)
def post(self, request):
context = self.get_context_data()
form = context['question_form']
import pdb; pdb.set_trace()
if form.is_valid():
question = form.save(commit=False)
question.user_account = request.user.account
question.save()
form.save_m2m()
return HttpResponseRedirect(
reverse("questions:question", kwargs={'id': question.id})
)
return self.render_to_response(context)
class Question(models.Model):
title = models.CharField(max_length=50)
body = models.TextField()
dated = models.DateField(default=date.today)
likes = models.IntegerField(default=0)
user_account = models.ForeignKey(
'users.UserAccount',
on_delete=models.SET_NULL,
null=True, blank=True,
related_name="questions"
)
tags = models.ManyToManyField(Tag, related_name='questions')
objects = models.Manager()
dateranges = DateRangeQuerySet.as_manager()
status = QuestionStatusQuerySet.as_manager()
class Meta:
ordering = ['-dated']
default_manager_name = "objects"
def __str__(self):
return self.title
class TestPostQuestionPageAdded(TestCase):
'''Verify that a User has succesfully added a question'''
#classmethod
def setUpTestData(cls):
cls.user = User.objects.create_user("Me")
cls.account = UserAccount.objects.create(user=cls.user)
tag1 = Tag.objects.create(name="tag1")
tag2 = Tag.objects.create(name="tag2")
cls.data = mock_questions_submitted[1]
cls.data.update(
{'tags': [tag1, tag2]}
)
def test_user_submitted_question_added(self):
total_questions = Question.objects.count()
self.client.force_login(self.user)
response = self.client.post(
reverse("questions:create"),
data=self.data,
)
question = Question.objects.all().get()
self.assertEqual(response.status_code, 302)
self.assertEqual(Question.objects.count(), 1)
self.assertEqual(question.user_account, self.account)
{'_state': <django.db.models.base.ModelState object at 0x0000024F9A49D788>, 'id': None,
'title': 'What is a primary key?',
'body': "<p>Upon studying database relationships, a relation happens to be set of records\nof the same type that is stored in the database. Yet, I'm not understanding\nwhat purpose the primary key serves? How does a record benefit from having\na primary key?</p>",
'dated': datetime.date(2021, 4, 26),
'likes': 0, 'user_account_id': 1}
(Pdb) question.tags
*** ValueError: "<Question: What is a primary key?>"
needs to have a value for field "id" before this many-to-many relationship can be used.
I would like to add an additional field for user's details. But it has to use another value outside of database fields.
For more clearly like this
model:
class User(models.Model):
id= models.AutoField(primary_key=True)
last_name= models.CharField(max_length=20)
first_name=models.CharField(max_length=20)
role_id = models.IntegerField()
serializer:
class UserSerializer(serializers.ModelSerializer):
display_summary = serializers.SerializerMethodField()
login_user_id = serializers.IntegerField(required=False)
class Meta:
model = User
fields = ("id","last_name","first_name", "display_summary", "login_user_id")
def get_display_summary(self, obj):
login_id = self.validated_data.get('login_user_id', None)
login_user = User.objects.filter(pk=login_id).first()
if obj.role_id==2 and login_user.role_id==1:
return 1
return 0
So in my views, when getting just one user, it's all ok:
#api_view(['GET'])
#login_required
def get_user(request, login_user, user_id):
serializer = UserSerializer(User.objects.get(pk=user_id), data={'login_user_id': login_user.id})
if serializer.is_valid():
result = serializer.data
return Response(result, status=status.HTTP_200_OK)
#result:
#{
# "id": 2,
# "last_name": "V",
# "first_name": "Lich",
# "role_id": 2,
# "display_summary": 1
#}
But when I need to return a list, how can I add additional data (login_user_id)?
This is not working:
users = User.objects.filter(last_name__icontains='v')
result_serializer = UserSerializer(users, data={'login_user_id': login_user.id}, many=True)
return result_serializer.data
The error occur say that it's looking for a list, not a dict for inputted param.
Based on #Hai Lang 's idea, I made a new namedtuple to add more info. Something like this:
Serializer:
No use ModelSerializer, use normal Serializer with field I would like to show on this view
class UserSummarySerializer(serializers.Serializer):
user_id = serializers.SerializerMethodField()
last_name = serializers.SerializerMethodField()
first_name = serializers.SerializerMethodField()
display_summary = serializers.SerializerMethodField()
def validate(self, data):
#some validation if needed
return data
def get_user_id(self, obj):
return obj.user.user_id
def get_last_name(self, obj):
return obj.user.last_name
def get_first_name(self, obj):
return obj.user.first_name
def get_display_summary(self, obj):
login_user = obj.login_user
if obj.role_id==2 and login_user.role_id==1:
return 1
return 0
then in my views:
userSummary = namedtuple('userSummary ', ('user', 'login_user'))
users = User.objects.filter(last_name__icontains='v')
objs = [userSummary(u, login_user) for u in users]
result_serializer = UserSerializer(objs, many=True)
return result_serializer.data
Please help me out of this django REST framework issue .I am trying to create on rest api which which will populate data in two tables based on argument passed .
My models look like .
class WorkloadType(models.Model):
type = models.CharField(max_length=60)
status = models.BooleanField(default=True)
class Workload(models.Model):
workload_type = models.ForeignKey(WorkloadType)
workload_group = models.IntegerField(blank=True, null=True)
name = models.CharField(max_length = 60)
hdd_gb = models.IntegerField(blank=True, null=True)
ssd_gb = models.IntegerField(blank=True, null=True)
iops = models.CharField(max_length = 60)
vcpu = models.IntegerField(blank=True, null=True)
cpu = models.IntegerField(blank=True, null=True)
created_date = models.DateTimeField(auto_now_add=True, blank=True)
status = models.BooleanField(default=True)
class VdiWorkload(models.Model):
workload = models.ForeignKey(Workload)
provision_type = models.CharField(max_length = 60)
number_of_users = models.IntegerField()
json_data = models.CharField(max_length=255,blank = True,null = True)
status = models.BooleanField(default=True)
created_date = models.DateTimeField(auto_now_add=True, blank=True)
I have my serializer.py file which is looking like .
from django.forms import widgets
from rest_framework import serializers
from models import WorkloadType,Workload, \
VdiWorkload,DbWorkload,\
VmWorkload,ExchangeWorkload,RawWorkload
class VdiSerializer(serializers.Serializer):
class Meta:
model = VdiWorkload
class WorkloadSerializer(serializers.Serializer):
vdi = VdiSerializer(required = False)
pk = serializers.IntegerField(read_only=True)
workload_group = serializers.IntegerField(required=False)
workload_type = serializers.CharField(max_length = 10)
name = serializers.CharField(max_length = 60)
hdd_gb = serializers.IntegerField()
ssd_gb = serializers.IntegerField()
iops = serializers.CharField(max_length = 60)
vcpu = serializers.IntegerField()
cpu = serializers.IntegerField()
class Meta:
model = Workload
fields = ('vdi','workload_group','workload_type','name','hdd_gb','ssd_gb','iops','vcpu','cpu')
def create(self, validated_data):
"""
Create and return a new `Workload` instance, given the validated data.
"""
wt = WorkloadType.objects.get(type = validated_data['workload_type'])
validated_data['workload_type'] = wt
print "=-=-=-=-=-=-=-=-=-=-=-=-=------"
if not validated_data['workload_group']:
workload_group = Workload.objects.latest('workload_group')
validated_data['workload_group'] = workload_group
else:
try:
workload_group = Workload.objects.latest('workload_group')
validated_data['workload_group'] = workload_group + 1
except:validated_data['workload_group'] = 1
#try:node_exist = Workload.objects.get(name = validated_data['name'])
#except:node_exist = None
#if node_exist:
# raise serializers.ValidationError('Node name already exist.')
#else:
wl = Workload.objects.create(**validated_data)
VdiWorkload.objects.create(workload=wl, **validated_data)
return wl
Now I passing the rest body like.
{
"type": "Exchange",
"iops": "22",
"name": "another model",
"hdd_gb": "23",
"ssd_gb": "320",
"hdd_count": "123",
"ssd_count": "4",
"memory": "123",
"core": "114",
"rackspace": "6",
"vcpu":"12",
"workload_type":"VDI",
"workload_group":true,
"cpu":"1",
"vdi":[{
"provision_type":"user",
"number_of_users":"10",
"json_data":"any extra data which we want"
}]
}
But whenever I am making post request I am getting the error
Traceback
raceback:
File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py" in get_response
111. response = callback(request, *callback_args, **callback_kwargs)
File "/usr/local/lib/python2.7/dist-packages/django/views/decorators/csrf.py" in wrapped_view
77. return view_func(*args, **kwargs)
File "/home/rohit/workspace/sizer/src/sizer/Workload/views.py" in workload_list
49. serializer.save()
File "/usr/local/lib/python2.7/dist-packages/rest_framework/serializers.py" in save
164. self.instance = self.create(validated_data)
File "/home/rohit/workspace/sizer/src/sizer/Workload/WorkloadSerializer.py" in create
63. return Workload.objects.create(**validated_data)
File "/usr/local/lib/python2.7/dist-packages/django/db/models/manager.py" in create
137. return self.get_query_set().create(**kwargs)
File "/usr/local/lib/python2.7/dist-packages/django/db/models/query.py" in create
375. obj = self.model(**kwargs)
File "/usr/local/lib/python2.7/dist-packages/django/db/models/base.py" in __init__
367. raise TypeError("'%s' is an invalid keyword argument for this function" % kwargs.keys()[0])
Exception Type: TypeError at /workload/workloadlist/
Exception Value: 'vdi' is an invalid keyword argument for this functi
Please do let me know if you need any other info I am still not able to solve out this question .
I am not able to identify this issue as I am new in DRF .
Please do let me know what might I am doing wrong here .My views looklike .
#csrf_exempt
def workload_list(request):
"""
List all code users, or create a new user.
"""
print "Herer I am *****************************111111"
if request.method == 'GET':
print "Herer I am *****************************"
workloads = Workload.objects.all()
serializer = WorkloadSerializer(workloads, many=True)
return JSONResponse(serializer.data)
elif request.method == 'POST':
data = JSONParser().parse(request)
serializer = WorkloadSerializer(data=data)
print serializer
print "****************** I am in ***********views now "
if serializer.is_valid():
serializer.save()
return JSONResponse(serializer.data, status=201)
return JSONResponse(serializer.errors, status=400)
Forgive me if this is off track but it looks to me like the model doesn't know anything about the nested field (vdi). Did you try popping it off validated_data?
Here's a small (untested) example of what I have been working on using djangorestframework==3.1.3 with Django==1.7.
models.py:
class Child(models.Model):
child_data = models.CharField(max_length=255)
class Parent(models.Model):
# Set 'related_name' to the nested field name in the serializer.
child_id = models.ForeignKey(Child, related_name='child')
serializers.py:
class ChildSerializer(serializers.ModelSerializer):
class Meta:
model = Child
class ParentSerializer(serializers.ModelSerializer):
# The ForeignKey in the model is supplied with this name so that GET
# requests can populate this and the source field below makes it
# accessible in create after validation.
child = TestInfoSerializer(source='child_id')
class Meta:
model = Parent
fields = ('child', )
def create(self, validated_data):
child_data = validated_data.pop('child_id') # strip out child_id for subsequent Parent create
try:
# try to find an existing row to fulfill the foreign key
child_instance = Child.objects.filter(**child_data)[0]
except IndexError:
# create a new row
child_instance = Child.objects.create(**child_data)
return Parent.objects.create(child_id=child_instance, **validated_data)
With this I can POST nested JSON without thinking about the foreign key:
{
"child": {
"child_data": "asdf"
}
}
GET also returns the nested representation with this setup.
I hope this helps.