Django Rest Serializer: Reverse relationships - django

Disclaimer: I am learning django as I apply it to a database / PHP app i inherited. The database is a bit of a mess, with no foreign key constrains, and inconsistent naming. I do not want to touch or redo anything on the database because I don't want to mes with the legacy application at all.
Stack: Python 2.7. Django 1.5, Django Rest Framework
The problem is that I have a relationship where there is an Idea that has multiple Tickers. The tickers table has the foreign key to the ideas (teaser_id) so that we have something like
**Tickers**
id teaser_id
1 1
2 1
3 1
4 2
4 2
**Ideas**
id
1
2
I had django generate the model from the database, but without the FK Constraints it didn't generate all the relationships properly. So here is haw the models are configured:
class Tickers(models.Model):
id = models.IntegerField(primary_key=True)
# I changed to this
teaser_id = models.ForeignKey(Idea)
# From
# teaser_id = models.IntegerField(null=True, blank=True)
ticker = models.CharField(max_length=135L, blank=True)
date_added = models.CharField(max_length=135L, blank=True)
class Meta:
db_table = 'teaser_tickers'
class Idea(models.Model):
id = models.IntegerField(primary_key=True)
industry_id = models.IntegerField()
post_type = models.CharField(max_length=45L)
class Meta:
db_table = 'idea'
Here are my serializers
class TickerSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = myModels.Tickers
fields = (
'id'
,'teaser_id'
,'ticker'
)
class IdeaSerializer(serializers.HyperlinkedModelSerializer):
user = UserSerializer(many=False, read_only=True)
ticker = TickerSerializer(many=True, read_only=True, )
teaser = myFields.TeaserField(teaser_length=200, original_field='content')
class Meta:
model = myModels.Idea
fields = (
'id'
, 'title'
, 'date_added'
, 'user'
, 'teaser'
, 'ticker'
)
I want the Ideas Resource to return the tickers as a child node set.
The REST request is for the Idea where the tickers is a child element. So I am getting an exception that ticker isn't defined in idea. Fine get that - but I am just guessing on how to set this up at this point - I am sludging through documentation and source - but was hoping someone could help me out.
THank you

As akaphenom said you have to use the related_name in your serializer, but since you don't specify any in your models you must use the default, in this case teaser_set and your IdeaSerializer must be:
class IdeaSerializer(serializers.HyperlinkedModelSerializer):
user = UserSerializer(many=False, read_only=True)
tickers = ReverseTickerSerializer(many=True, read_only=True)
teaser_set = myFields.TeaserField(teaser_length=200, original_field='content')
class Meta:
model = myModels.Idea
fields = (
'id',
'title',
'date_added',
'user',
'teaser_set',
'tickers',
)

SO the solution for the reverse lookup is specifying the model correctly, and namely the related_name which is the field that is created in the foreign model to perform the reverse look up. Now I specified a custom serializer to limit to the content I am interested in - but that piece is optional.
class Tickers(models.Model):
id = models.IntegerField(primary_key=True)
# I changed to this
teaser_id = models.ForeignKey(Idea, related_name='tickers')
# From
# teaser_id = models.IntegerField(null=True, blank=True)
ticker = models.CharField(max_length=135L, blank=True)
date_added = models.CharField(max_length=135L, blank=True)
class Meta:
db_table = 'teaser_tickers'
class Idea(models.Model):
id = models.IntegerField(primary_key=True)
industry_id = models.IntegerField()
post_type = models.CharField(max_length=45L)
class Meta:
db_table = 'idea'
Here are my serializers
class ReverseTickerSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = myModels.Tickers
fields = (
'id'
,'ticker'
)
class IdeaSerializer(serializers.HyperlinkedModelSerializer):
user = UserSerializer(many=False, read_only=True)
tickers = ReverseTickerSerializer(many=True, read_only=True)
teaser = myFields.TeaserField(teaser_length=200, original_field='content')
class Meta:
model = myModels.Idea
fields = (
'id'
, 'title'
, 'date_added'
, 'user'
, 'teaser'
, 'tickers'
)

Related

How to return data from two different tables in django?

I have two models in my models.py. I need to return a json response which includes data from two tables.
How should my view and serializer look like?
class Device(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
device_name = models.CharField(max_length=200, null=True, blank=True )
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.device_name)
class StatusActivity(models.Model):
OFFLINE = 1
ONLINE = 2
STATUS = (
(OFFLINE, ('Offline')),
(ONLINE, ('Online')),
)
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
device_id = models.ForeignKey(Device, related_name='StatusActivity', on_delete=models.CASCADE)
changed_to = models.PositiveSmallIntegerField(choices=STATUS)
modified_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.device_id)
Expected Response:
{
"device_id":"",
"device_name":"",
"changed_to":"",
"modified_at":"",
}
UPDATE:
I set my views.py and serializer.py as below. I am checking
Serializer.py
class DeviceSerializer(serializers.ModelSerializer):
class Meta:
model = Device
fields = '__all__'
class StatusActivitySerializer(serializers.ModelSerializer):
class Meta:
model = StatusActivity
fields = '__all__'
class ListSerializer(serializers.Serializer):
# devices = DeviceSerializer(many=True)
# activities = StatusActivitySerializer(many=True)
class Meta:
model = [Device, StatusActivity]
fields = ['device_id', 'device_name', 'changed_to', 'modified_at']
Views.py
class DeviceListView(generics.ListAPIView):
queryset = Device.objects.all()
serializer_class = ListSerializer
class StatusActivityListView(generics.ListAPIView):
queryset = StatusActivity.objects.all()
serializer_class = StatusActivitySerializer
Actually you don't need to have two separated views for this, because you can easily serialize relations from one serializer class.
Take a look at this useful answer: How do I include related model fields using Django Rest Framework?
For your case you can write something like this:
class StatusActivitySerializer(serializers.ModelSerializer):
device_name = serializers.CharField(source='device_id.device_name')
class Meta:
model = StatusActivity
fields = ('changed_to', 'modified_at', 'device_id', 'device_name')
Something that worth to note:
it's a good idea for ForeignKey field use device name instead of
device_id;
related_name arg should have a name for reverse access. Keep it
meaningful, e.g. status_activities is a good choice.
filter the status activity. you can refer the foreignkey.
eg:
items = []
for statusact in StatusActivity.objects.all():
items.append({
"device_id":statusact.device_id.id,
"device_name":statusact.device_id.device_name,
"changed_to":statusact.changed_to,
"modified_at":statusact.modified_at,
})

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)

Django, DRF (& React) Foreign key, how to display the name value instead of the foreign key id

There are two Django models - ClientCompany & Proposal and the foreign key of ClientCompany is within the Proposal model. In Proposal how do I display the name of the ClientCompany instead of the foreign key id?
models.py:
class ClientCompany(models.Model):
name = models.CharField("Client Name", max_length=255)
abn_acn = models.BigIntegerField("ABN / ACN")
def __str__(self):
return self.name
class Proposal(models.Model):
proj_name = models.CharField("Project Name", max_length=255)
loc_state = models.CharField(
max_length=3,
)
proj_type = models.CharField(
max_length=30,
)
prop_status = models.CharField(
max_length=20,
)
client = models.ForeignKey(ClientCompany, on_delete=models.CASCADE)
oneic = models.ForeignKey(
User, on_delete=models.CASCADE, related_name='main_engineer')
twoic = models.ForeignKey(
User, on_delete=models.CASCADE, related_name='second_engineer')
created_at = models.DateTimeField(default=datetime.now)
def __str__(self):
return self.proj_name
serializers.py:
class ClientSerializer(serializers.ModelSerializer):
class Meta:
model = ClientCompany
fields = ('id', 'name', 'abn_acn')
class ProposalSerializer(serializers.ModelSerializer):
class Meta:
model = Proposal
fields = ('id', 'proj_name', 'loc_state', 'proj_type', 'prop_status', 'client', 'oneic', 'twoic',)
queryset api.py:
class ProposalViewSet(viewsets.ModelViewSet):
permission_classes = [permissions.IsAuthenticated, ]
queryset = Proposal.objects.all()
serializer_class = ProposalSerializer
currentlyshows the client foreign key id
I've been stuck on this, tried to apply the existing solutions recommended for similar problems but had no luck... if someone can tell me what I'm missing - thanks
I found this worked in the end by adding the serializers.SlugRelatedField line in serializers.py:
class ProposalSerializer(serializers.ModelSerializer):
client = serializers.SlugRelatedField(slug_field="name", read_only=True)
class Meta:
model = Proposal
fields = ('id', 'proj_name', 'loc_state', 'proj_type', 'prop_status',
'client', 'oneic', 'twoic',)
Update your serializer like this:
class ProposalSerializer(serializers.ModelSerializer):
client = ClientSerializer()
class Meta:
model = Proposal
fields = ('id', 'proj_name', 'loc_state', 'proj_type', 'prop_status', 'client', 'oneic', 'twoic',)

select related in 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

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()