Django REST: ignoring custom fields which are not part of model - python

My TimeReport model looks like this:
class TimeReport(models.Model):
minutes_spent = models.PositiveIntegerField()
task = models.ForeignKey(Task, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
modified_at = models.DateTimeField(auto_now=True)
reported_for = models.DateField()
note = models.TextField(null = True, blank=True)
status = models.CharField(max_length=50, choices=State.choices, default=State.new)
user = models.ForeignKey(User, on_delete=models.PROTECT)
And my model serializer:
class TimeReportCreateSerializer(serializers.ModelSerializer):
class Meta:
model = TimeReport
fields = (
'id',
'minutes_spent',
'reported_for',
'note',
'status',
'task_custom_id',
)
task_custom_id = serializers.CharField()
def create(self, validated_data):
user = User.objects.get(auth_user_id = self.context['user_id'])
task = Task.objects.filter(custom_id = validated_data['task_custom_id']).filter(user = user.id).first()
report = TimeReport(**validated_data)
report.user = user
report.task = task
report.save()
return report
So, the problem is, that I want to take a custom value in a serializer, which is not a part of a model and do some custom logic with it - in this case search for the right 'task' in the database. But when I try to parse the model by using report = TimeReport(**validated_data), it gives me an exception:
TypeError at /api/report/
TimeReport() got an unexpected keyword argument 'task_custom_id'
Im kind of new to Django and python itself, so - what is the best approach?

If you are going to use that field only for creation, you should use write_only option.
task_custom_id = serializers.CharField(write_only=True)
See the docs here https://www.django-rest-framework.org/api-guide/fields/#write_only

You just need to remove task_custom_id from the dictionary
class TimeReportCreateSerializer(serializers.ModelSerializer):
class Meta:
model = TimeReport
fields = (
'id',
'minutes_spent',
'reported_for',
'note',
'status',
'task_custom_id',
)
task_custom_id = serializers.CharField()
def create(self, validated_data):
user = User.objects.get(auth_user_id = self.context['user_id'])
task_custom_id = validated_data.pop("task_custom_id")
task = Task.objects.filter(custom_id = task_custom_id).filter(user = user.id).first()
report = TimeReport(**validated_data)
report.user = user
report.task = task
report.save()
return report

task = Task.objects.filter(custom_id = validated_data.pop('task_custom_id')).filter(user = user.id).first()
the **validated_data will return (task_custom_id=value, field1=value1 ...) and task_custom_id it's not a TimeReport field so all u need is to pop it from validated_data before calling the constructor TimeReport

Related

Django serializer, nested relation and get_or_create

I've been bugging on this issue for some time now. I have two models : Acquisitions and RawDatas.
Each RawData have one Acquisition, but many RawDatas can have the same Acquisition.
I want to create or get the instance of Acquisition automatically when I create my RawDatas. And I want to be able to have all informations using the serializer.
class Acquisitions(models.Model):
class Meta:
unique_together = (('implant', 'beg_acq', 'duration_acq'),)
id = models.AutoField(primary_key=True)
implant = models.ForeignKey("Patients", on_delete=models.CASCADE)
beg_acq = models.DateTimeField("Beggining date of the acquisition")
duration_acq = models.DurationField("Duration of the acquisition")
class RawDatas(models.Model):
class Meta:
unique_together = (('acq', 'data_type'),)
id = models.AutoField(primary_key=True)
acq = models.ForeignKey("Acquisitions", on_delete=models.CASCADE)
data_type = models.CharField(max_length=3)
sampling_freq = models.PositiveIntegerField("Sampling frequency")
bin_file = models.FileField(db_index=True, upload_to='media')
And my serializers are these :
class AcquisitionSerializer(serializers.ModelSerializer):
class Meta:
model = Acquisitions
fields = ('id', 'implant', 'beg_acq', 'duration_acq')
class RawDatasSerializer(serializers.ModelSerializer):
acq = AcquisitionSerializer()
class Meta:
model = RawDatas
fields = ('id', 'data_type', 'sampling_freq', 'bin_file', 'acq')
def create(self, validated_data):
acq_data = validated_data.pop('acq')
acq = Acquisitions.objects.get_or_create(**acq_data)
RawDatas.objects.create(acq=acq[0], **validated_data)
return rawdatas
My problem is that, using this, if my instance of Acquisitions already exists, I get a non_field_errors or another constraint validation error.
I would like to know what is the correct way to handle this please ?
So I can automatically create this using the nested serializer, and when I only want to have informations (such as a GET request), I can have all the field I need (every field of the two models).
Thanks in advance for your help !
Try this:
class AcquisitionSerializer(serializers.ModelSerializer):
class Meta:
model = Acquisitions
fields = ('id', 'implant', 'beg_acq', 'duration_acq')
class RawDatasSerializer(serializers.ModelSerializer):
class Meta:
model = RawDatas
fields = ('id', 'data_type', 'sampling_freq', 'bin_file', 'acq')
def create(self, validated_data):
acq_data = validated_data.pop('acq')
acq = Acquisitions.objects.filter(id=acq_data.get('id')).first()
if not acq:
acq = AcquisitionSerializer.create(AcquisitionSerializer(), **acq_data)
rawdata = RawDatas.objects.create(acq=acq, **validated_data)
return rawdata

Manager object has no attribute 'save'

In my serializers.py I have a OrderCreateSerializer:
class OrderCreateSerializer(ModelSerializer):
data_params = serializers.DictField() # 根据产品数据模型不同而异
class Meta:
model = Order
fields = (
"product_describe", # 产品描述 (购买xx产品 + 参数)
"billing_type", # 计费类型 ("包年包月")
"data_params", # 数据
)
def create(self, validated_data):
request = self.context.get("request")
if request and hasattr(request, "user"):
user = request.user
validated_data["order_num"] = generateOrderNum(userid=user.id)
validated_data["order_status"] = "未支付"
validated_data["order_status"] = "未支付"
data_dic = validated_data.pop("data_params") #
# data_dic["data"]["profile"]
validated_data["buytime"] = data_dic["data"]["buytime"]
validated_data["count"] = data_dic["data"]["count"]
validated_data["paytype"] = ""
validated_data["cost"] = ""
validated_data["account"] = user.account
return Order.objects.save(**validated_data) # this is the line 57
When I save the validated_data, it report the bellow error:
Manager object has no attribute 'save'
My Order model is like bellow, there is many fields in it :
class Order(models.Model):
"""
订单
"""
order_num = models.CharField(max_length=128, unique=True) # 订单编号
order_status = models.CharField(max_length=12) # 订单状态 "未支付", "已支付,未完成", "已完成", "已经删除","其他"
product_describe = models.TextField() # 产品描述
billing_type = models.CharField(max_length=16) # 计费类型
buytime = models.CharField(max_length=16) # 比如:1月 永久
count = models.IntegerField() # 购买数量
paytype = models.CharField(max_length=16) # 支付方式(支付包,微信,xxx)
cost = models.DecimalField(max_digits=8, decimal_places=2, default=0.00) # 费用(需要花费多少钱)
account = models.ForeignKey(to=Account) # 所属账户
ctime = models.DateTimeField(auto_now_add=True) # 创建时间
uptime = models.DateTimeField(auto_now=True) # 更新时间
def __str__(self):
return self.product_describe
def __unicode__(self):
return self.product_describe
I don't know why there is the Manager object here.
You're calling save on the manager (ie, objects)
return Order.objects.save(**validated_data)
You call save on models.
I assume you're trying to create the model, in which case you want create.
return Order.objects.create(**validated_data)
Order.objects is an instance of the Manager class. The save method is a method of the model class.
Try:Order(**validated_data).save()

Django rest framework - NOT NULL constraint on a foreign Key

I have this Error :
IntegrityError at /api/post_flight_schedule/
NOT NULL constraint failed: flights_tailnumber.aircraft_type_id
When I try to add a new PosFlightSchedule object to DB over http://127.0.0.1:8000/api/pos_flight_schedule (Website/APIView)
I have the below serializer :
class PosFlightScheduleModelSerializer(ModelSerializer):
class Meta:
model = PosFlightSchedule
fields = ['pos_route_id', 'tail_number', 'pos_flight_number', 'pos_flight_departure_time', 'pos_flight_date',
'pax_count']
class PosFlightScheduleSerializer(serializers.Serializer):
pos_route_id = serializers.CharField(source='pos_route_id.route_id', read_only=False)
tail_number = serializers.CharField(source='tail_number.tail_number', read_only=False)
pos_flight_number = serializers.CharField(source='pos_flight_number.flight_number', read_only=False)
pos_flight_departure_time = serializers.CharField(source='pos_flight_departure_time.flight_departure_time', allow_null=True,
read_only=False)
pos_flight_date = serializers.CharField(source='pos_flight_date.flight_date', read_only=False)
pax_count = serializers.IntegerField(read_only=False)
def create(self, validated_data):
tail_number_data = validated_data.pop("tail_number")
tail_number = TailNumber.objects.create(**tail_number_data)
flight_number_data = validated_data.pop("pos_flight_number")
flight_number = FlightSchedule.objects.create(**flight_number_data)
flight_departure_time_data = validated_data.pop("pos_flight_departure_time")
print "DEP_TIME" + str(flight_departure_time_data)
flight_departure_time = FlightSchedule.objects.create(**flight_departure_time_data)
route_id_data = validated_data.pop("pos_route_id")
route_id = FlightScheduleDetail.objects.create(**route_id_data)
flight_date_data = validated_data.pop("pos_flight_date")
flight_date = FlightScheduleDetail.objects.create(**flight_date_data)
pax_count = validated_data.pop("pax_count")
schedule_obj = PosFlightSchedule.objects.create(**validated_data)
# if tail_number:
schedule_obj.set_tail_number(tail_number)
schedule_obj.set_pos_flight_number(flight_number)
schedule_obj.set_pos_flight_departure_time(flight_departure_time)
schedule_obj.set_pos_route_id(route_id)
schedule_obj.set_pos_flight_date(flight_date)
schedule_obj.set_pax_count(pax_count)
schedule_obj.save()
return schedule_obj
def update(self, instance, validated_data):
tail_number = validated_data.pop("tail_number")
flight_number = validated_data.pop("pos_flight_number")
flight_departure_time = validated_data.pop("pos_flight_departure_time")
route_id = validated_data.pop("pos_route_id")
flight_date = validated_data.pop("pos_flight_date")
pax_count = validated_data.pop("pax_count")
instance.__dict__.update(validated_data)
if tail_number:
instance.set_tail_number(tail_number)
if flight_number:
instance.set_pos_flight_number(flight_number)
if flight_departure_time:
instance.set_pos_flight_departure_time(flight_departure_time)
if route_id:
instance.set_pos_route_id(route_id)
if flight_date:
instance.set_pos_flight_date(flight_date)
if pax_count:
instance.set_pax_count(pax_count)
instance.save()
return instance
The model of the field which is giving error looks like :
class TailNumber(models.Model):
tail_number_id = models.AutoField(null=False, primary_key=True)
tail_number = models.CharField(max_length=20, null=False, blank=False, unique=True)
aircraft_type = models.ForeignKey(AircraftType, null=False, blank=False)
def __unicode__(self):
return u'%s' % self.tail_number
class Meta:
verbose_name_plural = "Tail Numbers"
I am not understanding what is going wrong here.
The error you get is probably due to the fact that the dictionary tail_number_data does not contain the keyword aircraft_type, which is expected by TailNumber.objects to create the row in the db, since you defined it with no possibility to be null
aircraft_type = models.ForeignKey(AircraftType, null=False, blank=False)
^^^^^
Check that the key "aircraft_type" does exist in the dictionary tail_number_data, or allow for it to be null. Furthermore, if you consider the latter option and that this information is supposed to come from a UI, you may also want to allow for aircraft_type to be blank. See differentiate null=True, blank=True in django for details.

How to compare the changes in a modelform with model (django)

I do not know how to compare if a modelform is equal to a model in django.
thank you very much
models.py
class Person(models.Model):
name = models.CharField(max_length=45)
lastname = models.CharField(max_length=45)
dni = models.BigIntegerField()
email = models.EmailField(max_length=30)
status = models.BooleanField()
departament = models.ForeignKey(Departament) #char
forms.py
class Form_Person(forms.ModelForm):
class Meta:
model = models.Person
fields = ['name', 'lastname', 'dni', 'address', 'phone', 'email', 'position', 'status', 'departament']
views.py
#auth.decorators.login_required(login_url='login')
def persons_person(request,id='id'):
page_name = 'Persons'
try:
person = models.Person.objects.get(id=id)
list_departaments = models.Departament.objects.all()
list_departaments = list_departament.exclude(name = person.departament)
if request.method == 'POST':
form_person = forms.Form_Person(request.POST, initial='person')
Here the comparison would be implemented
### code to compare ###
# if form_persona.is_valid() and form_person.has_changed(): #Something like that
# ***how to compare***
# form_person.save()
except models.Person.DoesNotExist as e:
person = None
list_departaments = None
return render(request, 'app/persons/person.html',
{'page_name':page_name,
'person':person,
'list_departaments':list_departaments})
The link in the duplicate flag suggests using save method on object (same can be done in form also). I would personally suggest using signals with pre_save option to check before saving.

Django Rest Framework - how to write multiple nested field serializer (for reading & writing)

I'm trying to write a "def create" method to perform nested serialization for multiple objects.
def create(self, validated_data):
suggested_songs_data = validated_data.pop('suggested_songs')
suggest_song_list = list()
for song_data in suggested_songs_data:
song = Song.objects.create(**song_data)
suggest_song_list.append(song)
message = Messages.objects.create(suggested_songs=suggest_song_list, **validated_data)
return message
Here is my schema:
class MessagesSerializer(serializers.HyperlinkedModelSerializer):
id = serializers.IntegerField(source='pk', read_only=True)
suggested_songs = SongSerializer(many=True)
class Meta:
model = Messages
fields = ('id','owner','url','suggested_songs',)
#fields = ('id','url','suggested_songs',)
class SongSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Song
fields =('id','title','artist','album','albumId','num_votes','cleared')
read_only_fields = ('song_id')
But I am getting this error
Cannot assign "[<Song: Song object>, <Song: Song object>]": "Messages.suggested_songs" must be a "Song" instance.
Any advice?
EDIT:
Here is the model.
class Messages(models.Model):
owner = models.OneToOneField(User, primary_key=True, related_name='user_messages', editable=False) #TODO, change owner to 'To'
#suggested_songs = models.ForeignKey(Song, null=True, blank=True)
suggested_songs = models.ManyToManyField(Song, related_name='suggested_songs')
You can't create manyToMany relations without the objects already created. You must first create the objects and then make the relation.
Something like:
def create(self, validated_data):
suggested_songs_data = validated_data.pop('suggested_songs')
message = Messages.objects.create(**validated_data)
for song_data in suggested_songs_data:
song = Song.objects.create(**song_data)
message.suggested_songs.add(song)
return message

Categories