DRF post request multiple inner serializers - django

i have three models named Smoker,Switch,Survey i have smoker as foreign key in Switch model and switch as foreign key in Survey model
class Smoker(models.Model):
first_name = models.CharField(max_length=50, blank=True, null=True)
last_name = models.CharField(max_length=50, blank=True, null=True)
mobile = models.IntegerField(null=True, blank=True)
gender = models.BooleanField(blank=True, null=True)
age = models.ForeignKey(Age,models.DO_NOTHING,blank=True, null=True)
occupation = models.ForeignKey(Occupation, models.DO_NOTHING, blank=True, null=True)
class Switch(models.Model):
time = models.TimeField(blank=True, null=True)
count_outers = models.IntegerField(blank=True, null=True)
count_packs = models.IntegerField(blank=True, null=True)
smoker = models.ForeignKey(Smoker, models.DO_NOTHING, blank=True, null=True)
new_brand = models.ForeignKey(NewBrand, models.DO_NOTHING, blank=True, null=True)
new_sku = models.ForeignKey(NewSku, models.DO_NOTHING, blank=True, null=True)
# def __str__(self):
# return self.time.strftime("%H:%M")
class Survey(models.Model):
user = models.ForeignKey(User, models.DO_NOTHING, blank=True, null=True)
date = models.DateField(null=True, blank=True)
bool_switch = models.BooleanField(null=True, blank=True)
reason = models.ForeignKey(Reason, models.DO_NOTHING, null=True, blank=True)
shift = models.ForeignKey(ShiftingTime, models.DO_NOTHING, null=True, blank=True)
current_brand = models.ForeignKey(CurrentBrand, models.DO_NOTHING, null=True, blank=True)
current_sku = models.ForeignKey(CurrentSku, models.DO_NOTHING, null=True, blank=True)
pos = models.ForeignKey(Pos, models.DO_NOTHING, null=True, blank=True)
switch = models.ForeignKey(Switch, models.DO_NOTHING, null=True, blank=True)
and here i have my serializers:
class SmokerSerializer(serializers.ModelSerializer):
class Meta:
model = Smoker
fields = '__all__'
class SwitchSerializer(serializers.ModelSerializer):
smoker = SmokerSerializer()
class Meta:
model = Switch
fields = '__all__'
def create(self, validated_data):
smoker_data = validated_data.pop('smoker', None)
if smoker_data:
smoker = Smoker.objects.create(**smoker_data)
validated_data['smoker'] = smoker
return Switch.objects.create(**validated_data)
class SurveySerializer(serializers.ModelSerializer):
switch = SwitchSerializer()
class Meta:
model = Survey
fields = '__all__'
def create(self, validated_data):
switch_data = validated_data.pop('switch', None)
if switch_data:
switch = Switch.objects.create(**switch_data)
validated_data['switch'] = switch
return Survey.objects.create(**validated_data)
and i make a generic for for Creating and listing all the survey
class SurveyCreateAPIView(generics.ListCreateAPIView):
def get_queryset(self):
return Survey.objects.all()
serializer_class = SurveySerializer
for each displayed survey i have to display switch data related to it and inside the switch object i need to display the smoker object inside it so each survey object must look like this
{
"id": 11,
"switch": {
"id": 12,
"smoker": {
"firstname":"sami",
"lastname:"hamad",
"mobile":"7983832",
"gender":"0",
"age":"2",
"occupation":"2"
},
"time": null,
"count_outers": 5,
"count_packs": 7,
"new_brand": 2,
"new_sku": 2
},
"date": "2018-12-08",
"bool_switch": true,
"user": 7,
"reason": 3,
"shift": 2,
"current_brand": 6,
"current_sku": 4,
"pos": 2
},
but when i make a POST request it is giving me this error
ValueError at /api/v2/surveysync/ Cannot assign
"OrderedDict([('first_name', 'aline'), ('last_name', 'youssef'),
('mobile', 7488483), ('gender', False), ('age', ),
('occupation', )])": "Switch.smoker" must be
a "Smoker" instance.
so please help and thank you so much!

You're going along the right path but you're saving the switch objects manually instead of allowing the SwitchSerializer do it for you. Same thing with create method in switch serializer. It should be this way:
class SmokerSerializer(serializers.ModelSerializer):
class Meta:
model = Smoker
fields = '__all__'
class SwitchSerializer(serializers.ModelSerializer):
smoker = SmokerSerializer()
class Meta:
model = Switch
fields = '__all__'
def create(self, validated_data):
smoker_data = validated_data.pop('smoker', None)
if smoker_data:
serializer = SmokerSerializer(data=smoker_data, context=self.context)
if serializer.is_valid():
validated_data['smoker'] = serializer.save()
return super().create(validated_data)
class SurveySerializer(serializers.ModelSerializer):
switch = SwitchSerializer()
class Meta:
model = Survey
fields = '__all__'
def create(self, validated_data):
switch_data = validated_data.pop('switch', None)
if switch_data:
serializer = SwitchSerializer(data=switch_data, context=self.context)
if serializer.is_valid():
validated_data['switch'] = serializer.save()
return super().create(validated_data)

In SwitchSerializer you defined the create function as a method of the inner Meta class and not as a member of SwitchSerializer class. Try this
class SwitchSerializer(serializers.ModelSerializer):
smoker = SmokerSerializer()
class Meta:
model = Switch
fields = '__all__'
def create(self, validated_data):
smoker_data = validated_data.pop('smoker', None)
if smoker_data:
smoker = Smoker.objects.create(**smoker_data)
validated_data['smoker'] = smoker
return Switch.objects.create(**validated_data)

Related

Get a list of all the field values from a Foreign Key Table in Django

am new to Django and currently trying the Foreign Key concept. I have three models as shown below.
class Basket(models.Model):
basket_name = models.CharField(max_length=5, unique=True)
def __str__(self):
return self.basket_name
class Product(models.Model):
Grams = 'GM'
Kilograms = 'KG'
WeightBased = 'WPG'
QuantityBased = 'CPG'
PRODUCT_UNIT_WT_CHOICES=[
(Grams, 'Grams'),
(Kilograms, 'Kilograms')
]
PRODUCT_TYPE_CHOICES =[
(WeightBased, 'Weight Based Product'),
(QuantityBased, 'Quantity Based Product')
]
product_name = models.CharField(max_length=30, unique=True)
product_description = models.TextField(max_length=300)
product_price = models.DecimalField(max_digits=5, decimal_places=2)
product_unit_weight = models.DecimalField(max_digits=5, decimal_places=2)
product_unit_weight_units = models.CharField(max_length=2, choices=PRODUCT_UNIT_WT_CHOICES, default=Grams)
product_type = models.CharField(max_length=3, choices=PRODUCT_TYPE_CHOICES, default=QuantityBased)
product_image = models.ImageField(upload_to=imageUploadPath, null=True, blank=True)
def __str__(self):
return self.product_name
class BasketProductMapping(models.Model):
basket_reference = models.ForeignKey(Basket, on_delete=models.CASCADE)
product_reference = models.ForeignKey(Product, on_delete=models.CASCADE)
mapped_basket_name = models.CharField(max_length=5,null=False, blank=False)
mapped_product_name = models.CharField(max_length=30, null=False, blank=False)
Here are my serializers:
class ProductSerializer(serializers.ModelSerializer):
product = serializers.CharField(read_only=True)
class Meta:
model = Product
fields = ['id', 'product_name', 'product_description', 'product_price', 'product_unit_weight',
'product_unit_weight_units', 'product_type', 'product_image']
class BasketSerializer(serializers.ModelSerializer):
basket = serializers.CharField(read_only=True)
class Meta:
model = Basket
fields = ('id', 'basket_name')
class BasketProductMappingSerializer(serializers.ModelSerializer):
basket_reference = serializers.CharField(source ='basket.basket_name', read_only=True)
product_reference = serializers.CharField(source='product.product_name', read_only=True)
class Meta:
model = BasketProductMapping
fields = ['id', 'basket_reference', 'product_reference', 'mapped_basket_name', 'mapped_product_name']
What I am trying to achieve is get a list of all the values in 'basket_name' and 'product_name' when I call the BasketProductMappingSerializer. But the output I am getting is this:
{
"status": 1,
"message": "Basket Product Mapping List",
"data": [
{
"id": 1,
"mapped_basket_name": "A1",
"mapped_product_name": "XYZ"
}
]
}
This is my views.py code:
class BasketProductViewSet(APIView):
def get(self, request):
if request.GET.get('id'):
print('Basket Product Mapping Details')
basketProductMappingData = BasketProductMapping.get(id = request.GET.get('id'))
serializer = BasketProductMappingSerializer(basketProductMappingData)
else:
basketProductMappingData = BasketProductMapping.objects.all().values('id', 'basket_reference__basket_name', 'product_reference__product_name', 'mapped_basket_name', 'mapped_product_name')
serializer = BasketProductMappingSerializer(basketProductMappingData, many=True)
response = {'status':1, 'message':"Basket Product Mapping List", 'data':serializer.data}
Where am I going wrong? Sorry if my question is very trivial. Thanks for your help in advance.
i see that there is an error in your views BasketProductMapping.get(id = request.GET.get('id')) should be BasketProductMapping.objects.get(id = request.GET.get('id'))
i don't know if it gonna work but can you try
basket_reference = serializers.ReadOnlyField(source ='basket.basket_name')
product_reference = serializers.ReadOnlyField(source='product.product_name')
instead of
basket_reference = serializers.CharField(source ='basket.basket_name', read_only=True)
product_reference = serializers.CharField(source='product.product_name', read_only=True)

Query django foreignkey relationship

I am developing an audit management information system where I can record all finding related to an audit. I have models with foreignkeys relationship. How do I see all findings with a particular assignment and audit_title and unit?
See relevant codes below.
model.py content
class Unit(models.Model):
unit_name = models.CharField(max_length=30, blank=True, null=True)
def __unicode__(self):
return self.unit_name
class Assignment(models.Model):
assignment_name = models.CharField(max_length=30, blank=True, null=True)
def __unicode__(self):
return self.assignment_name
class Task(models.Model):
task_title = models.CharField(max_length=35, blank=True, null=True)
return self.task_title
class Finding(models.Model):
assignment = models.ForeignKey(Assignment, blank=True, null=True)
audit_title = models.ForeignKey(Task, blank=True, null=True)
auditor = models.ManyToManyField(User, blank=True)
unit = models.ForeignKey(Unit, blank=True, null=True)
audit_period = models.DateField(auto_now_add=False, auto_now=False, blank=True, null=True)
contact_person = models.CharField('Contact Person', max_length=500, blank=True, null=True)
finding = models.TextField('Detail Finding', max_length=500, blank=True, null=True)
be = models.CharField(max_length=30, blank=True, null=True)
form.py
class FindingSearchForm(forms.ModelForm):
class Meta:
model = Finding
fields = ['assignment',
'audit_title',
'unit',
'be',
]
Am I have the following in my views.py but I have this error invalid literal for int() with base 10: ''
views.py content
def finding_list(request):
title = 'List of Finding'
queryset = Finding.objects.all()
queryset_count = queryset.count()
form = FindingSearchForm(request.POST or None)
context = {
"title": title,
"form": form,
"queryset_count": queryset_count,
}
if request.method == 'POST':
unit = form['unit'].value()
audit_title = form['audit_title'].value()
assignment = form['assignment'].value()
queryset = Finding.objects.all().order_by('-timestamp').filter(be__icontains=form['be'].value(),
unit_id=unit,
assignment_id=assignment,
audit_title_id=audit_title,)
if request.method == 'POST':
unit = form['unit'].value()
audit_title = form['audit_title'].value()
assignment = form['assignment'].value()
queryset = Finding.objects.all().order_by('-timestamp').filter(be__icontains=form['be'].value()
)
if (unit != ''):
queryset = queryset.filter(unit_id=unit)
if (audit_title != ''):
queryset = queryset.filter(audit_title_id=audit_title)
if (assignment != ''):
queryset = queryset.filter(assignment_id=assignment)

How do I make sure entered integer is greater than current value before updating model field?

I am using a form that saves to one model to update the most current mileage which is stored in another model. I want to make sure the mileage entered is > or = the current mileage. I havent been able to figure out the right validation or where to write the validation.
I have tried an if statement in the form_valid() of the CreateView and a save() method in the model.
Models.py
class Vehicle(models.Model):
name = models.CharField(blank=True, max_length=100)
make = models.CharField(blank=True, max_length=100)
model = models.CharField(blank=True, max_length=100)
year = models.IntegerField(blank=True, null=True)
vin = models.CharField(blank=True, max_length=17)
gvw = models.IntegerField(blank=True, null=True)
license_plate = models.CharField(blank=True, max_length=100)
purchase_date = models.DateField()
current_mileage = models.IntegerField(blank=True, null=True)
class Meta:
ordering = ['name']
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('vehicles:vehicle_detail', kwargs={'pk':self.pk})
#property
def get_current_mileage(self):
return self.current_mileage
class FuelEntry(models.Model):
vehicle = models.ForeignKey(Vehicle, on_delete=models.CASCADE)
date = models.DateTimeField(auto_now_add=True)
fuel_choices = (
('EMPTY', 'Empty'),
('1/8', '1/8'),
('1/4', '1/4'),
('1/2', '1/2'),
('3/4', '3/4'),
('FULL', 'Full'),
)
current = models.CharField(max_length=5, choices=fuel_choices)
after = models.CharField(max_length=5, choices=fuel_choices, blank=True)
gallons = models.DecimalField(decimal_places=2, max_digits=5, blank=True, default='0')
cost = models.DecimalField(decimal_places=2, max_digits=5, blank=True, default='0')
mileage = models.IntegerField(blank=False)
user = models.ForeignKey(User, on_delete=models.CASCADE)
class Meta:
ordering = ['-date', 'vehicle']
def __str__(self):
return self.vehicle.name
def get_absolute_url(self):
return reverse('fuellog:entry_detail', kwargs={'pk':self.pk})
Views.py
class CreateEntry(CreateView):
model = FuelEntry
fields = ('vehicle', 'current', 'after', 'gallons', 'cost', 'mileage')
def form_valid(self,form):
self.object = form.save(commit=False)
self.object.user = self.request.user
vehicle_id = self.object.vehicle.pk
mileage = self.object.mileage
self.object.save()
current_mileage = Vehicle.objects.filter(id=vehicle_id).get('current_mileage')
if current_mileage > mileage:
raise ValidationError('Incorrect mileage reading')
Vehicle.objects.filter(id=vehicle_id).update(current_mileage=mileage)
return super().form_valid(form)
ValueError at /fuel/new
too many values to unpack (expected 2)

missing fields of base class django serializer

I have few classes:
class Correspondent(models.Model):
title = models.CharField(max_length=60)
class Subject(models.Model):
title = models.CharField(max_length=60)
class Letter(models.Model):
correspondent = models.ForeignKey(Correspondent,
on_delete=models.SET_NULL, null=True)
subject = models.ForeignKey(Subject, on_delete=models.SET_NULL, null=True)
description = models.TextField(max_length=600, blank=True)
case_number = models.CharField(max_length=100, blank=True)
inventory_number = models.CharField(max_length=100, blank=True)
note = models.TextField(max_length=300, blank=True)
user = models.ForeignKey(User, editable=False, on_delete=models.SET_NULL, null=True)
date = models.DateField(default=datetime.date.today)
date_creation = models.DateTimeField(auto_now_add=True)
class IncomingLetter(Letter):
response_to = models.ForeignKey('OutgoingLetter', on_delete=models.SET_NULL, null=True, blank=True)
outgoing_number = models.CharField(max_length=120, blank=True)
incoming_number = models.CharField(max_length=120, blank=True)
class OutgoingLetter(Letter):
response_to = models.ForeignKey(IncomingLetter, on_delete=models.SET_NULL, null=True, blank=True)
outgoing_number = models.CharField(max_length=120, blank=True)
and in a view i trying to send json with serialized class B instances:
from django.http import JsonResponse
from django.core import serializers
from .models import IncomingLetter
def index(request):
....
some_data = IncomingLetter.objects.all()
serialized_data = serializers.serialize('json', some_data)
return JsonResponse(json.dumps(serialized_data), safe=False)
and response missed all Letter fields in json. Is there a way to not serialize it manually?
serialized_data == {"model": "letters.incomingletter", "pk": 4, "fields": {"response_to": 3, "outgoing_number": "12312321", "incoming_number": "123123"}}
SOLVED # (not sure)
I wrote simple serializer, but not sure is this cleanest way(many unnecessary fields, but it easy to clean):
class LetterSerializer:
def __init__(self):
self.serialized_data = {}
def serialize(self, data):
for idx, instance in enumerate(data):
serialized_instance_data = {}
for key, value in instance.__dict__.items():
serialized_instance_data[key] = str(value)
self.serialized_data[idx] = serialized_instance_data
return self.serialized_data

Django rest framework - cant serialize query set

I try to serialize query set
def do(self):
reservations = Reservation.objects.all()
serializer = ReservationSerializer(data=reservations, many=True)
if serializer.is_valid():
encoded_data = json.dumps(serializer.data)
r = requests.post('http://httpbin.org/post', headers={'Content-Type': 'application/json'}, data=encoded_data)
print(r.text)
else:
print(serializer.errors)
And I always get error of
{u'non_field_errors': [u'Expected a list of items but got type "QuerySet".']}
I tried to use values() on query set, and then convert to list, but this way I get objects without nested models
model
class Reservation(models.Model):
start = models.DateField(verbose_name='Заезд', auto_now=False, auto_now_add=False, blank=False)
end = models.DateField(verbose_name='Выезд', auto_now=False, auto_now_add=False, blank=False)
check_in_time = models.TimeField(verbose_name='Время заезда', blank=False)
check_out_time = models.TimeField(verbose_name='Время выезда', blank=False)
has_refund = models.BooleanField(verbose_name='Возвратная бронь', default=True)
payed = models.BooleanField(verbose_name='Оплачено', default=False)
reserved_days = models.ManyToManyField(Day, blank=False)
additional_services = models.ManyToManyField(AdditionalService)
guest_name = models.CharField(verbose_name='Имя гостя', max_length=200, blank=True)
reservation_number = models.CharField(verbose_name='Номер брони', max_length=200, blank=True)
class AdditionalService(models.Model):
payment_date = models.CharField(verbose_name='Дата оплаты', max_length=200, blank=True)
payment_type = models.CharField(verbose_name='Форма оплаты', max_length=200, blank=False)
service = models.CharField(verbose_name='Услуга', max_length=200, blank=False)
quantity = models.IntegerField(blank=False)
price = models.FloatField(blank=False)
class Day(models.Model):
date = models.DateField(auto_now=False, auto_now_add=False)
price = models.FloatField()
payment_method = models.CharField(max_length = 200, blank=True)
payment_date = models.CharField(max_length=200, blank=True)
room = models.ForeignKey(Room, null=True, blank=True, verbose_name='Номер', on_delete=models.CASCADE)
class Room(models.Model):
name = models.CharField(max_length = 200, null=True)
id = models.AutoField(primary_key=True)
room_id = models.CharField(max_length = 200, null=False)
def __unicode__(self):
return self.name
serializers
class ReservationSerializer(serializers.ModelSerializer):
reserved_days = DaySerializer(many=True)
additional_services = AdditionalServicesSerializer(many=True)
class Meta:
model = Reservation
fields = [
'start',
'end',
'check_in_time',
'check_out_time',
'reserved_days',
'additional_services',
'has_refund',
'payed',
'guest_name',
'reservation_number',
]
class DaySerializer(serializers.ModelSerializer):
room = RoomSerializer()
class Meta:
model = Day
fields = [
'date',
'price',
'payment_method',
'payment_date',
'room',
]
class AdditionalServicesSerializer(serializers.ModelSerializer):
class Meta:
model = AdditionalService
fields = [
'payment_date',
'payment_type',
'service',
'quantity',
'price',
]
class RoomSerializer(serializers.ModelSerializer):
class Meta:
model = Room
fields = [
'room_id',
]
For serialization you don't need to use data keyword, just pass queryset as first positional argument:
serializer = ReservationSerializer(reservations, many=True)
return serializer.data