Django.rest_framework: How to serialize one to many to many? - python

I have some troubles serializing with django.
I have three models, let's say a School, a Room and a Desk (dummy name for example).
Each School have multiple Room, and each Room have multiple Desk.
The classes and their relations look like this :
class School(models.Model):
name = models.CharField()
class Room(models.Model):
name = models.CharField()
school_id = models.ForeignKey(School)
class Desk(models.Model):
row = models.IntegerField()
col = models.IntegerField()
room_id = models.ForeignKey(Room)
I want to be able to serialize a list of School, each directly containing all the desks inside.
The closet I got was by writing in my serialize.py three serializer :
class DeskSerializer(serializers.ModelSerializer):
class Meta:
field = (row, col,)
class RoomSerializer(serializers.ModelSerializer):
desks = DeskSerializer(source='desk_set', many=True)
class Meta:
field = (desks,)
class SchoolSerializer(serializers.ModelSerializer):
rooms = RoomSerializer(source='room_set', many=True)
class Meta:
field = (name, rooms,)
Which return a list of school containing a list of room containing a list of desk, when I want a list of School containing a list of desk
Which source should I use in the School serializer to return directly the desk? Something like source='room_set.desk_set'? Or maybe by using a transform_ function?
PS: the code is write from scratch on the post, please ignore the possible syntax errors

If I'm understanding you correctly, you want the SchoolSerializer to return a nested structure 2 levels deep, but skipping the intermediate model. To do this, I would create a method in your School model to retrieve the Desk instances:
class School(models.Model):
...
def get_desks(self):
rooms = Room.objects.filter(school=self)
desks = Desk.objects.filter(room__in=rooms)
return desks
Then, in your SchoolSerializer include a field that uses that method and renders the returned instances as you wish via your DeskSerializer:
class SchoolSerializer(serializers.ModelSerializer):
...
desks = DeskSerializer(
source='get_desks',
read_only=True
)
class Meta:
field = (name, desks)
The key to understanding how this works is that the model method used as the value for the source argument must simply return instances of that serializer's model. The serializer takes it from there, rendering the object(s) however you defined them within that serializer (in this case the DeskSerializer).
Hope this helps.

Related

DjangoModelFactory with a SubFactory field that does not create new entries but points to existing ones instead

Let's say I have:
class CompanyFactory(DjangoModelFactory):
class Meta:
model = Company
name = factory.Faker("company")
address = factory.Faker("address")
class InvoiceFactory(DjangoModelFactory):
class Meta:
model = Invoice
company = factory.SubFactory(CompanyFactory)
num = factory.Faker("numerify", text="#"*10)
value_total = factory.Faker("random_number", digits=3)
When I run the InvoiceFactory.create() method a new Invoice entry is created. And because Invoice "links" (foreign key) to Company, a new Company entry is also created.
Question:
What would be the right way of rewriting this so that when calling InvoiceFactory.create() an existing company entry is picked, instead of creating a new one?
You have two options:
Always pick an existing company:
class InvoiceFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.Invoice
company = factory.Iterator(models.Company.objects.all())
The iterator is evaluated lazily, when the first invoice is created. However, it will always cycle through the same factories.
Create some companies, then reuse:
First, add a django_get_or_create attribute to your CompanyFactory: if the provided name already exists in the database, the existing instance will be reused:
class CompanyFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.Company
django_get_or_create = ['name']
name = factory.Faker('company')
address = factory.Faker('address')
Then, provide values for the name field from a constant list (here using factory.fuzzy.FuzzyChoice):
class InvoiceFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.Invoice
company = factory.SubFactory(
CompanyFactory,
name=factory.fuzzy.FuzzyChoice(['PSF', 'Django', 'The Spanish Inquisition']),
)
Once the 3 first companies have been created, they will be reused for all following objects.

Add property in Django many to many relation

I have the following Django models:
class Lesson(models.Model):
title = models.TextField()
class Course(models.Model):
lessons = models.ManyToManyField(Lesson)
class User(AbstractUser):
favorites = models.ManyToManyField(Lesson)
I have a route /courses/course_id that returns a course details including an array of lessons (using Django Rest Framework)
How can i return in the lessons object an additional attribute favorite based on my users favorites.
I attempted the following:
course = self.get_object(course_id)
favorites = request.user.favorites
for lesson in course.lessons.all():
if lesson in favorites.all():
lesson.favorite = True
serializer = CourseDetailSerializer(course, context=serializer_context)
return Response(serializer.data)
But when returning it doesn't work:
(django.core.exceptions.ImproperlyConfigured: Field name favorite is
not valid for model Lesson.
My serializers:
class CourseDetailSerializer(serializers.HyperlinkedModelSerializer):
lessons = LessonListSerializer(many=True, read_only=True)
class Meta:
model = Course
fields = ('id', 'lessons', 'name', 'title')
class LessonSerializer(serializers.ModelSerializer):
class Meta:
model = Lesson
fields = ('id', 'title', 'duration', 'favorite')
You cannot add properties to objects if they are not defined, like here:
lesson.favorite = True
When you create m2m relation:
favorites = models.ManyToManyField(Lesson)
... django creates virtual model that simply stores pairs of primary keys from both models. This relation could look like this in database:
id | user_id | lesson_id
------+---------------+----------
151 | 11 | 3225
741 | 21 | 4137
What I think you want to achieve is to add extra information about this relation.
Therefore you need to create intermediary model with that extra field, i.e:
class User(AbstractUser):
favorites = models.ManyToManyField(Lesson, through='UserLessons')
class UserLessons(models.Model):
user = models.ForeignKey(User)
lesson = models.ForeignKey(Lesson)
favorite = models.BooleanField(default=False)
Your lessonmodel doesn't include a favourite boolean, so it isn't able to set it when you call lesson.favorite = True
If you want to get rid of the error, try:
class Lesson(models.Model):
title = models.TextField()
favorite = models.BooleanField(initial=False)
Although it appears that the lessons aren't user-specific. So this solution might not be what you are looking for, because it will set a Lesson's "favourite" field to be true for all users if only one user sets it as a favorite.

Dynamic field value in Django class

I want to create one dynamic field value for my class in Django using PyCharm.
CATEGORY_CHOICES = (
('on','one'),
('tw','two'),
('th','three'),
('fo','four'),
('fi','five'),
)
class art(models.Model):
Title=models.CharField(max_length=300)
Desciption=models.TextField()
Category=models.CharField(max_length=2, choices=CATEGORY_CHOICES)
I want the category field in my class to take more than one option, maybe two or more.
Any help would be appreciated.
If you want one python model to have multiple categories, then you need django ManyToManyField. Basically one model object could have multiple choices, one choice can also belong to multiple models objects:
class Category(models.Model):
category_name = models.CharField(max_length=10, unique=True)
class Art(models.Model):
title = models.CharField(max_length=300)
description = models.TextField()
category = models.ManyToManyField('Category', blank=True)
Note that I put unique=True for category_name to avoid creating duplicate categories.
Something not related, you shouldn't use lower fist in model name, and upper first for field name, that's really BAD naming convention and might confuse others who read your code.
Example:
# create your category in code or admin
one = Category.objects.create(category_name='one')
two = Category.objects.create(category_name='two')
three = Category.objects.create(category_name='three')
# create a new art obj
new_art = Art.objects.create(title='foo', description='bar')
# add category to Art obj
new_art.category.add(one)
new_art.category.add(two)
# category for new art obj
new_art_category = new_art.category.all()
# get only a list of category names
category_names = new_art_category.values_list('category_name', flat=True)
# create another Art obj
new_art2 = Art.objects.create(title="test", description="test")
# assign category to new_art2
new_art2.category.add(two)
new_art2.category.add(three)
Django doc for many to many and python pep8 doc.

Seeking help in designing django models

I am looking for some feedback on a django model for a project I'm working on.So I'm building a document database where the documents can be split into 3 categories - GTO,EWR and QPR.Each of these documents correspond to a well.Each well can have multiple documents associated with it.The users can upload and view documents corresponding to a well.Here's my design :
basedoc - class to hold document's attributes and will serve as base class.
wells - class to hold well's attributes.
GTO - inherits from basedoc and is linked with wells using foreign key.
EWR - inherits from basedoc and is linked with wells using foreign key.
QPR - inherits from basedoc and is linked with wells using foreign key.
class basedoc(models.Model):
docfile = models.FileField(upload_to='documents/%Y/%m/%d')
title = models.CharField("Doc Title",max_length=50)
pub_date = models.DateTimeField('Date published',auto_now_add=True)
remark = models.TextField(max_length=200,blank=True)
publisher = models.ForeignKey(User)
def __str__(self):
return self.title
class wells(models.Model):
well_name = models.CharField(max_length=20)
well_loc = models.CharField(max_length=20)
def __str__(self):
return self.well_name
class GTO(basedoc):
gto = models.ForeignKey(wells)
pass
class EWR(basedoc):
ewr = models.ForeignKey(wells)
pass
class QPR(basedoc):
qpr = models.ForeignKey(wells)
pass
I initially used basedoc as an abstract base class,but changed because i wanted to return a list of all documents to the user as a view.Please help me in improving this design.Thanks.
You probably need to retrieve all the documents of a wells from time to time. Or you may need to move a document from GTO to EWR. To be efficient with that, I wouldn't use 3 tables but 1.
You can use choices :
TYPE_CHOICES = (
(1, 'GTO'),
(2, 'EWR'),
(3, 'QPR'),
)
class Document(models.Model):
# ...your other fields here...
type = models.IntegerField(choices=TYPE_CHOICES)

Ordering a django model on many-to-may field. Denormalization required?

I have a system for composing items from parts in certain categories
For instance take the following categories:
1: (Location)
2: (Material)
And the following parts:
Wall (FK=1)
Roof (FK=1)
Roof (FK=1)
Brick (FK=2)
Tile (FK=2)
Wood (FK=2)
To compose these items:
Wall.Brick, Roof.Wood, Wall.Wood
class Category(models.Model):
ordering = models.IntegerField()
desc = models.CharField()
class Part:
name = models.CharField()
category = models.ForeignKey(Category)
class Meta:
unique_together = ('name', 'category')
ordering = ['category','name']
class Item:
parts = ManyToManyField(Part)
def __unicode__(self):
return ".".join([p.name for p in self.parts.all()])
Now the question: how do i order the Items? I'd prefer to have them ordered ascending by the composed name, but dont know how.
One way of doing things could be an extra field for the name, that gets updated on the save() method. That would mean denormalizing the model...
If I understand correctly, sort key do not exist in database, so database cannot sort it (or at least on trivially, like using Django ORM).
Under those conditions, yes - denormalize.
It's no shame. As said, normalized dataset is for sissies...

Categories

Resources