select related in Django - django

I am trying to accomplish something very similar to:
How to join 3 tables in query with Django
Essentially, I have 3 tables. In the Django REST we are showing table 3. As you see below (models.py), table 3 has company_name which is a foreign key of table 2 and table 2 is a foreign key of table 1. Both table 2 and 3 are linked by the table 1 ID. Table 1 contains the actual text, which we want to display in the API output, not the ID number.
Table 1: Manufacturer of Car -- Table 2: What the Car is -- Table 3: list of all cars
Models.py
Table 1:
class ManufacturerName(models.Model):
name_id = models.AutoField(primary_key=True)
company_name = models.CharField(unique=True, max_length=50)
class Meta:
managed = False
db_table = 'manufacturer_name'
Table 2:
class CarBuild(models.Model):
car_id = models.AutoField(primary_key=True)
car_icon = models.CharField(max_length=150, blank=True, null=True)
company_name = models.ForeignKey('ManufacturerName', models.DO_NOTHING, db_column='ManufacturerName')
class Meta:
managed = False
db_table = 'car_build'
Table 3:
class CarList(models.Model):
list_id = models.AutoField(primary_key=True)
company_name = models.ForeignKey('CarBuild', models.DO_NOTHING, db_column='CarBuild')
title = models.CharField(unique=True, max_length=100)
description = models.TextField()
class Meta:
managed = False
db_table = 'cars'
Within my views:
This is what I am trying, based on the foreign key relationships:
queryset = CarList.objects.all().select_related('company_name__company_name')
I get no errors when I save and run this, however, the ID is still being returned, and not the text associated with the foreign key relationships:
[
{
"list_id": 1,
"company_name": "http://127.0.0.1:8000/api/1/",
"title": "Really fast car you're driving, and this is dummy text",
Again, I would like to achieve getting the text associated with the company_name foreign key relationships from table 1 to show in the JSON.
serializer and viewset
class manufacturer_name(serializers.HyperlinkedModelSerializer):
class Meta:
model = manufacturer_name
fields = ('name_id', 'company_name')
class manufacturer_name(viewsets.ModelViewSet):
queryset = manufacturer_namee.objects.all()
serializer_class = manufacturer_name
class CarBuildViewSet(viewsets.ModelViewSet):
queryset = CarBuild.objects.all()
serializer_class = CarBuildSerialiser
class CarBuildSerialiser(serializers.HyperlinkedModelSerializer):
class Meta:
model = CarBuild
fields = ('car_id', 'car_icon', 'company_name')
class CarListSerialiser(serializers.HyperlinkedModelSerializer):
class Meta:
model = News
fields = ('list_id', 'company_name', 'title')
class CarListViewSet(viewsets.ModelViewSet):
serializer_class = CarList
def get_queryset(self):
queryset = News.objects.all().select_related('company_name__company_name')
return queryset

Based on detailed conversation to clear few details. Here is the answer.
You need to make small changes to your models as it was quite confusing to understand what you want to achieve.
Models:
class ManufacturerName(models.Model):
name_id = models.AutoField(primary_key=True)
company_name = models.CharField(unique=True, max_length=50)
class Meta:
managed = False
db_table = 'manufacturer_name'
class CarBuild(models.Model):
car_id = models.AutoField(primary_key=True)
car_icon = models.CharField(max_length=150, blank=True, null=True)
manufacturer = models.ForeignKey(ManufacturerName,on_delete=models.SET_NULL)
class Meta:
managed = False
db_table = 'car_build'
class CarList(models.Model):
list_id = models.AutoField(primary_key=True)
car = models.ForeignKey(CarBuild, on_delete=models.DO_NOTHING)
title = models.CharField(unique=True, max_length=100)
description = models.TextField()
class Meta:
managed = False
db_table = 'cars'
And then You need to adjust your serializers.
class CarListSerialiser(serializers.HyperlinkedModelSerializer):
company_name= serializers.SerializerMethodField(read_only=True)
class Meta:
model = CarList
fields = ('list_id', 'company_name', 'title')
def get_company_name(self, obj):
return obj.car.manufacturer.company_name
And you use it in your view:
class CarListViewSet(viewsets.ModelViewSet):
queryset = CarList.object.all()
serializer_class = CarListSerialiser

Related

I want to post a list of JSON values to a model's field in Django

I want to post a movie into the collection's movie field( list of movies).
I define the model as
class Movie(models.Model):
# collection = models.ForeignKey(Collection, on_delete = models.CASCADE) #, related_name='reviews'
title = models.CharField(max_length=200)
description = models.CharField(max_length=200)
genres = models.CharField(max_length=200)
uuid = models.CharField(max_length=200)
def __str__(self):
return self.title
class Collection(models.Model):
title = models.CharField(max_length=200)
uuid = models.CharField(max_length=200, primary_key = True)
description = models.CharField(max_length=200)
movie = models.ForeignKey(Movie, on_delete = models.CASCADE)
def __str__(self):
return self.title
this is how i am using the viewset
class CollectionViewSet(viewsets.ModelViewSet):
queryset = models.Collection.objects.all()
serializer_class = serializers.CollectionSerializer
but i am not able to enter values for the movie field
enter image description here
also my serializer
class CollectionSerializer(serializers.ModelSerializer):
class Meta:
model = Collection
fields = '__all__'
By default, DRF will represent the relationship with a PrimaryKeyRelatedField, thus expecting a movie ID.
To achieve what you want (create an instance of movie with a collection), you need to overwrite the foreign key field in your serializer with your own Movie serializer.
class MovieSerializer(serializers.ModelSerializer):
class Meta:
model = Movie
fields = '__all__'
class CollectionSerializer(serializers.ModelSerializer):
movie = MovieSerializer()
class Meta:
model = Collection
fields = '__all__'
def create(self, validated_data):
movie = validated_data.pop('movie')
movie = Movie .objects.create(**movie )
collection = Collection.objects.create(movie=movie, **validated_data)
return collection
You need to overwrite the create method so when creating a Collection, you also create a movie.
However, I am not sure the foreign key is set in the right model in your model. (a movie belongs to many collection but not the other way around?) If that's not what you want, just reverse the logic for the serializer.
Edit:
Sending the following should work fine:
{ "uuid": "1001",
"title": "Action",
"description": "Action Movies",
"movie": { "title": "The Burkittsville 7",
"description": "The story of Rustin Parr.",
"genres": "Horror",
"uuid": "5e904"
}
}
The only problem as I mentionned earlier is in your model you defined the foreign key field in collection. So it expects one single movie instance and not a list, thus I took off the brackets you put around movie. Maybe you should consider setting the foreign key in the Movie model, or use a Many to many relationship.
#models.py
class Movie(models.Model):
# collection = models.ForeignKey(Collection, on_delete = models.CASCADE) #, related_name='reviews'
title = models.CharField(max_length=200)
description = models.CharField(max_length=200)
genres = models.CharField(max_length=200)
uuid = models.CharField(max_length=200)
def __str__(self):
return self.title
class Collection(models.Model):
title = models.CharField(max_length=200)
uuid = models.CharField(max_length=200, primary_key = True)
description = models.CharField(max_length=200)
movie = models.ManyToManyField(Movie, on_delete = models.CASCADE)
def __str__(self):
return self.title
serializers.py:
class MovieSerializer(serializers.ModelSerializer):
class Meta:
model = Movie
fields = '__all__'
class CollectionSerializer(serializers.ModelSerializer):
movie = MovieSerializer(read_only=True, many=True)
class Meta:
model = Collection
fields = '__all__'
hope this will give you better unserstand this will work for you

How to display many fields' values with ForeignKey relationship?

Looking for solution of this problem I encountered some similar threads, but referring to older versions of Django/DRF and thus not working in my case.
There are these two models:
class CsdModel(models.Model):
model_id = models.CharField("Item ID", max_length=8, primary_key=True)
name = models.CharField("Item Name", max_length=40)
active = models.BooleanField(default=True)
def __str__(self):
return self.model_id
class CsdListing(models.Model):
model_id = models.ForeignKey(CsdModel, on_delete=models.CASCADE, default=0, related_name='m_id')
name = models.ForeignKey(CsdModel, on_delete=models.CASCADE, default=0, related_name='m_name')
(...)
EDIT: Serializers are defined this way:
class CsdModelSerializer(serializers.ModelSerializer):
model_id = serializers.RegexField(regex='^\w{2}\d{3}$', allow_blank=False)
name = serializers.CharField(min_length=6, max_length=50, allow_blank=False)
class Meta:
model = CsdModel
fields = '__all__'
class CsdListingSerializer(serializers.ModelSerializer):
session_id = serializers.RegexField(regex='^s\d{2}$', allow_blank=False)
def validate_session_id(self, value):
(...)
class Meta:
model = CsdListing
fields = '__all__'
What I'd like to see, is model_id and name from CsdModel displayed inside a form created based on CsdListing model. But instead, the ID is duplicated:
How should I rebuild the model(s) to have both ID and name displayed in the form?
You should have only one foreign key. But the listing serializer should then reference the model as a nested serializer.
class CsdListing(models.Model):
model = models.ForeignKey(CsdModel, on_delete=models.CASCADE, default=0, related_name='listing')
class CsdListingSerializer(serializers.ModelSerializer):
model = CsdModelSerializer()
session_id = serializers.RegexField(regex='^s\d{2}$', allow_blank=False)

related objects queries django rest framework

I have the following models
class STUser(AbstractBaseUser):
email = models.EmailField(unique=True)
name = models.CharField(max_length=255)
companyname = models.CharField(max_length=200, blank=True, null=True)
...
class VenuePermissions(models.Model):
user = models.ForeignKey(STUser, on_delete=models.CASCADE)
venue = models.ForeignKey(Venue, on_delete=models.CASCADE)
signupvaildatestring = models.CharField(max_length=200, blank=True, null=True)
...
I want to grab all the STUser objects and grab all their permissions.
So what I would like is to grab all the VenuePermissions objects. And grab the user and venue object of each venuePermission
Two ways I can do this. use the VenuePermissions_set attribute on STUser but then how do I grab the venue when its just going to be a pk value?
Or focus on the VenuePermissions objects and grab the user and venue from the pk values but how?
I remember nested queries, and I kinda did one in my browse code.
here is an example:
rooms = Room.objects.filter(venue=OuterRef('pk'), sixtyroundseatingoption= True)
venuelist = venuelist.annotate(sixtyrounds=Exists(rooms))
venuelist = venuelist.filter(Q(sixtyrounds = True) | Q(fullbuyoutsixtyroundseatingoption = True))
I've done the set objects in a serializer before
Example serializer:
class RoomAndImageSerializer(serializers.ModelSerializer):
roomimage_set = RoomImageSerializer(many=True, read_only=True)
class Meta:
model = Room
fields = ('pk','name')
any help with this query would be appreciated!
So this is what I am currently trying, I will post an answer if this works:
class VenueUserList(ListAPIView):
serializer_class = VenueUserListSerializer
queryset = VenuePermissions.objects.select_related('user').select_related('venue').filter(signupvaildatestring=None)
class VenueUserListSerializer(serializers.ModelSerializer):
user = UserSerializer()
venue = VenueSerializer()
class Meta:
model = VenuePermissions
fields = ('user', 'venue', 'isvenueviewer', 'isvenueeventplanner', 'isvenueadministrator')
Here is the answer. However I still need to group venues by user. Working on that.
class VenueUserList(ListAPIView):
serializer_class = VenueUserListSerializer
queryset = VenuePermissions.objects.select_related('user').select_related('venue').filter(signupvaildatestring=None)
class VenueUserListSerializer(serializers.ModelSerializer):
user = UserSerializer()
venue = VenueSerializer()
class Meta:
model = VenuePermissions
fields = ('user', 'venue', 'isvenueviewer', 'isvenueeventplanner', 'isvenueadministrator')

How to serialize a self recursive many-to-many model using a through table in django rest_framework?

i am developing a rest API using django rest framework and i am stuck at a serializer the idea is to serialize a self recursive many to many model using a through table my code is:
model.py:
class Patient(models.Model):
class Meta:
db_table = 'patients'
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, unique=True)
id_card = models.CharField(max_length=45)
dob = models.DateField()
gender = EnumChoiceField(enum_class=Gender)
patientscol = models.CharField(max_length=45)
fk_user = models.ForeignKey(Users, related_name='user_patient', on_delete=models.CASCADE)
relative = models.ManyToManyField("self", through='PatientHasRelative')
class PatientHasRelative(models.Model):
class Meta:
db_table = 'patients_has_relatives'
fk_patient = models.ForeignKey(Patient, related_name='patient_has', on_delete=models.CASCADE)
fk_relative_patient = models.ForeignKey(Patient, related_name='patient_relative', on_delete=models.CASCADE)
relationship = EnumChoiceField(enum_class=Relationship)
my serializer.py is:
class PatientSerializer(serializers.ModelSerializer):
class Meta:
model = Patient
fields = ('__all__')
id = serializers.UUIDField(read_only=True)
id_card = serializers.CharField(required=True, max_length=45)
dob = serializers.DateField(required=True)
gender = EnumChoiceField(enum_class=Gender)
fk_user = serializers.PrimaryKeyRelatedField(required=True, queryset=Users.objects.all())
relative = PatientSerializer(read_only=True, required=True)#problem is here i cant use PatientSerializer here
class PatientHasRelativeSerializer(serializers.ModelSerializer):
class Meta:
model = PatientHasRelative
fields = ('__all__')
fk_patient = serializers.PrimaryKeyRelatedField(required=True, queryset=Patient.objects.all())
fk_relative_patient = serializers.PrimaryKeyRelatedField(required=True, queryset=Patient.objects.all())
relationship = EnumChoiceField(enum_class=Relationship)
a little help would be appreciated
To accomplish this you need to define related_name in the source model on the source field ie add
class Patient(models.Model):
relatives = models.ManyToManyField(
"self", through='PatientHasRelative', related_name='patients')
with this related_name you can easily access -- add/delete/set relatives/patients on either side of the relationships in the serializers
You can either do this using intermediary model
relative = Patient(**key_value_fields)
patient = Patient(**key_value_field)
PatientHasRelative.objects.create(
relative=relative, patient=patient, through_defaults=(relationship ='value',))
or you can do this
relative.patients.add(patient, through_defaults=relationship ='value')
or this
patient.relatives.add(relative, through_defaults=relationship ='value')
example retrieving
patient.relatives.all()

Nested serializer with Django-Rest-Framework

I've been trying to use a nested serializer with DRF but it won't display the related item in the output.
Here's my model.py :
class Categorie(models.Model):
nom = models.CharField(max_length=100)
def __unicode__(self):
return unicode(self.nom)
class Item(models.Model):
nom = models.CharField(max_length=100)
disponible_a_la_vente = models.BooleanField(default = True)
nombre = models.IntegerField()
prix = models.DecimalField(max_digits=5, decimal_places=2)
history = HistoricalRecords()
categorie = models.ForeignKey(Categorie, models.CASCADE)
class Meta:
verbose_name = "item"
verbose_name_plural = u"inventaire"
ordering = ['categorie', 'nom']
def __unicode__(self):
return u'{nom} - {nombre}'.format(nom = self.nom, nombre = self.nombre)
and my serializers.py
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = Item
fields = ('nom',)
class CategorieSerializer(serializers.ModelSerializer):
items = ItemSerializer(many=True)
class Meta:
model = Categorie
fields = ('nom', 'id', 'items')
The view i'm currently testing is very basic :
class InventaireDetail(generics.RetrieveAPIView):
queryset = Categorie.objects.all()
serializer_class = CategorieSerializer
but it gives the error:
AttributeError: Got AttributeError when attempting to get a value for
field items on serializer CategorieSerializer. The serializer
field might be named incorrectly and not match any attribute or key on
the Categorie instance. Original exception text was: 'Categorie'
object has no attribute 'items'.
I've been looking for a while now but i can't get it working even with the help of the doc.
Categorie.items does not exist. By default the reverse relation would get the name Categorie.item_set. You can fix that in two ways.
EITHER: add related_name to your foreign key.
class Item(models.Model):
categorie = models.ForeignKey(Categorie, models.CASCADE, related_name='items')
OR: another solution is to change the CategorieSerializer
class CategorieSerializer(serializers.ModelSerializer):
items = ItemSerializer(many = True, read_only=True, source='item_set')