Django Combine Two Tables Through a Third - django

If I have these 3 models
from django.db import models
class ModelA(models.Model):
name = models.CharField(max_length=50)
class ModelB(models.Model):
tag = models.CharField(max_length=50)
class ModelC(models.Model):
a = models.ForeignKey(ModelA)
b = models.ForeignKey(ModelB)
What is the best way to get all of my ModelA's and their associated ModelB.property's? I can't figure out a good way to do it without a raw query.
Ideally I'd be able to transform it into:
[
{
"name": "foo",
"tags": ["tag1", "tag2"]
}
....
]

Use related_name on the ForeignKeys
from django.db import models
class ModelA(models.Model):
name = models.CharField(max_length=50)
class ModelB(models.Model):
property = models.CharField(max_length=50)
class ModelC(models.Model):
a = models.ForeignKey(ModelA, related_name="theas")
b = models.ForeignKey(ModelB, related_name="thebs")
To get all ModelB instances that relate to an instance of ModelA
a_inst = ModelA.objects.get(foo=bar)
ModelB.objects.filter(thebs__a=a_inst)

Related

Is it possible to prefetch model with one query in this case?

Is it possible to prefetch Models B to Model A with one query or with minimal queries. I'm confused. Thanks.
from django.db import models
class ModelA(models.Model):
pass
class ModelB(models.Model):
pass
class ModelC(models.Model):
model_a = models.ForeignKey(ModelA, related_name="models_a", on_delete=models.CASCADE)
models_b = models.ManyToMany(ModelB, through="ModelD")
class ModelD(models.Model):
model_c = models.ForeignKey(ModelC, on_delete=models.CASCADE)
model_b = models.ForeignKey(ModelB, on_delete=models.CASCADE)
I'm do something like that, and it is work. But seems a bit ugly.
models_a_list = ModelsA.objects.all()
model_d_qs = ModelD.objects.select_related("model_c", "model_b")
model_d_map = defaultdict(list)
for d_item in model_d_qs:
model_d_map[d_item.model_c.model_a.id].append(d_item.model_b)
for a_item in models_a_list:
settatr(a_item, "model_b_set", model_d_map.get(a_item.id))
return models_a_list

Django Rest Framework reverse serializer

Please I need a little help. I have a model like below
class Person(models.Model):
name = models.CharField(max_length=100)
address = models.CharField(max_length=100)
class Employee(models.Model):
person = models.ForeignKey(Person, related_name='employee')
code = models.CharField()
In my EmployeeSerializer, how can I add the Person field
Something like:
class EmployeeSerializer(serializer.ModelSerializer):
person = # Something the get **Person** instance
code = serializers.IntegerField
class Meta:
model = Employee
fields = [
'id',
'person',
'code'
]
You can use depth option to get nested representation of related objects:
class EmployeeSerializer(serializer.ModelSerializer):
class Meta:
model = Employee
fields = [
'id',
'person',
'code'
]
depth = 1
If you need to customize nested object, you should use nested serializers as described here.

Django Rest Framework - how to get only one field from related models set

I have following models:
from django.db import models
class City(models.Model):
name = models.CharField(max_length=30)
last_update = models.DateTimeField(null=True)
class BusStop(models.Model):
city = models.ForeignKey(City, on_delete=models.CASCADE)
name = models.CharField(max_length=200, blank=True, default='')
Now using Django Rest Framework, I would like to create serializer that will return City details along with the list of all BusStops in the city - but I want the list to be only strings with BusStop names, like this:
{
"id": 1
"name": "City"
"last_update": "2019-09-19T22:13:54.851363Z"
"bus_stops": [
"stop1",
"stop2",
"stop3"
]
}
What I've tried so far is following serializers:
from rest_framework import serializers
class BusStopSerializer(serializers.ModelSerializer):
class Meta:
model = BusStop
fields = ('name', )
class CityDetailsSerializer(serializers.ModelSerializer):
busstop_set = BusStopSerializer(many=True)
class Meta:
model = City
fields = ('id', 'name', 'last_update', 'busstop_set')
But this creates list of objects with 'name' in them. So, how can I create a list with only BusStop names (as strings) in it?
Instead of the extra BusStopSerializer you could use a StringRelatedField:
# models.py
class BusStop(models.Model):
city = models.ForeignKey(City, on_delete=models.CASCADE)
name = models.CharField(max_length=200, blank=True, default='')
def __str__(self):
return self.name
# serializers.py
class CityDetailsSerializer(serializers.ModelSerializer):
bus_stops = StringRelatedField(many=True)
class Meta:
model = City
fields = ('id', 'name', 'last_update', 'bus_stops')
StringRelatedField, as recommended by wfehr, will only work as long as the __str__ method of BusStop only returns the name. An alternative is to use SlugRelatedField which allows you to specify a particular field from the related model, and has no dependency on __str__.
bus_stops = SlugRelatedField(many=True, slug_field='name')

Exporting a field from another model, using foreign key

I've just set up the whole import-export thing and I just can't make it export a field from another model, using the foreign key.
models.py
from django.db import models
from django.contrib.auth.models import User
from datetime import date
from .validators import validate_file_size
# Create your models here.
class CORMeserii(models.Model):
CodCOR = models.CharField(max_length=25, primary_key=True, unique=True)
MeserieCor = models.CharField(max_length=50, unique=True)
def __str__(self):
return str(self.CodCOR + " - " + self.MeserieCor)
class Meta:
verbose_name_plural = "CORuri"
class Oferta(models.Model):
solicitant = models.ForeignKey(User, on_delete=models.CASCADE)
cor = models.ForeignKey(CORMeserii, on_delete=models.CASCADE)
dataSolicitare = models.DateField(default=date.today)
locuri = models.IntegerField()
agentEconomic = models.CharField(max_length=50)
adresa = models.CharField(max_length=150)
dataExpirare = models.DateField()
experientaSolicitata = models.CharField(max_length=200)
studiiSolicitate = models.CharField(max_length=200)
judet = models.CharField(max_length=20)
localitate = models.CharField(max_length=25)
telefon = models.CharField(max_length=12)
emailContact = models.EmailField(max_length=40)
rezolvata = models.BooleanField(default=False)
def __str__(self):
return str(self.cor)
admin.py
from django.contrib import admin
from .models import Oferta, CORMeserii
from import_export import resources
from import_export.admin import ImportExportMixin, ImportExportModelAdmin
import tablib
# Register your models here.
class CorImEx(resources.ModelResource):
class Meta:
model = CORMeserii
class CorAdmin(ImportExportMixin, admin.ModelAdmin):
list_display = ('CodCOR', 'MeserieCor')
resource_class = CorImEx
class CorImExAdmin(ImportExportModelAdmin):
resource_class = CorImEx
class OferteImEx(resources.ModelResource):
class Meta:
model = Oferta
fields = ('id', 'solicitant', 'cor', 'oferta.cor.MeserieCor')
class OfertaAdmin(ImportExportMixin, admin.ModelAdmin):
list_display = ('id', 'solicitant', 'dataExpirare', 'dataSolicitare')
resource_class = OferteImEx
class OferteImExAdmin(ImportExportModelAdmin):
resource_class = OferteImEx
admin.site.register(Oferta, OfertaAdmin)
admin.site.register(CORMeserii, CorAdmin)
You can see it in the OferteImEx class - the field 'oferta.cor.MeserieCor'.
I want to export the MeserieCor field from the model CORMeserii - but I can't figure it out.
I tried with: 'oferta.cor.MeserieCor',
cor.MeserieCor', 'MeserieCor' (even though the last 2 ones don't make sense to me at all).
Is there any way to export that field somehow, even though it is from another model? (I'm pretty sure there is but I can't figure it out)
Thanks.
In Django you use double underscore (__) to follow relationship in lookups. It's in the documentation:
Django offers a powerful and intuitive way to “follow” relationships in lookups, taking care of the SQL JOINs for you automatically, behind the scenes. To span a relationship, just use the field name of related fields across models, separated by double underscores, until you get to the field you want.
Link: Lookups that span relationship
There is even an example in the django import export documentation how to follow relationship:
When defining ModelResource fields it is possible to follow model relationships:
class BookResource(resources.ModelResource):
class Meta:
model = Book
fields = ('author__name',)
Source: https://django-import-export.readthedocs.io/en/latest/getting_started.html#customize-resource-options

How to set up double nested serializer in Django Rest Framework

I am trying to figure out how to set up my nested serializers.
# models.py
class Product(models.Model):
sku = models.CharField()
product_name = models.CharField()
class Order(models.Model):
name = models.CharField()
address = models.CharField()
class OrderProduct(models.Model):
order = models.ForeignKey( Order )
product = models.ForeignKey( Product )
quantity = models.IntegerField()
So I want to have an api that can create an order in the form of the following:
{
"name" : "James",
"address" : "100 Main St",
"products" : [
{ "sku" : "1234", "quantity" : 1 }
]
}
I understand that I would need nest OrderProductSerializer inside OrderSerializer, but how do I implement it here when the "products" data use the field "sku" which is not found in the OrderProduct model. Do I do double-nesting? How does that look like?
# serializers.py
class OrderProductSerializer( serializers.ModelSerializer):
class Meta:
model = OrderProduct
exclude = ()
class OrderSerializer(serializers.ModelSerializer):
products = OrderProductsSerializer(many=True)
class Meta:
model = Order
exclude = ()
You are trying to implement Nested Serialize on reverse relationship. So, you have to explicitly provide the relationship name as the parameter to the serializer via source argument.
Try this
class OrderProductSerializer(serializers.ModelSerializer):
class Meta:
model = OrderProduct
fields = '__all__'
class OrderSerializer(serializers.ModelSerializer):
product = OrderProductSerializer(many=True, source='product_set')
class Meta:
model = Order
fields = '__all__'
For more info reffere these docs
1. DRF Nested Relationship
2. DRF-Reverse Realation
3. What is reverse-relationship in Django