Django Foreign Key Filter - django

I am defining some models for an inventory / work order application I am developing and I've run into a slight sticking point.
Here are some of the models that I'm currently having some difficulty with.
#models.py
from django.db import models
class Staff(models.Model):
ROLE = (
('M', 'Mechanic'),
('W', 'Warehouse'),
)
first_name = models.CharField(max_length=25)
last_name = models.CharField(max_length=25)
staff_role = models.CharField(max_length=1, choices=ROLE)
class WorkOrder(models.Model):
item_number = models.ForeignKey(Item)
date_started = models.DateField()
date_ended = models.DateField()
mechanic = models.ForeignKey(Staff)
What I would like is for a work order to only be associated with a staff member whose role is set as "Mechanic". Is there a way to restrict this within the model specification based on the models that I have here, or should I deal with this downstream when I set up the views and the forms?

Use the limit_choices_to parameter to ForeignKey:
mechanic = models.ForeignKey(Staff, limit_choices_to={'staff_role': 'M'})

Related

How to use prefetch_related in django rest api with foreying key and heritage

I am working on this Django project, it uses heritage and foreign keys for its models.
These are the models:
class SetorFii(models.Model):
name = models.CharField(max_length=255)
class Asset(models.Model):
category = models.ForeignKey(
Category, related_name='categories', on_delete=models.CASCADE)
ticker = models.CharField(max_length=255, unique=True)
price = models.FloatField()
class Fii(Asset):
setor_fii = models.ForeignKey(
SetorFii, null=True, default=None, on_delete=models.CASCADE, related_name="setor_fiis")
Class Crypto(Asset):
circulating_supply = models.FloatField(default=0)
class PortfolioAsset(models.Model):
asset = models.ForeignKey(Asset, on_delete=models.CASCADE)
I would like to get the field setor_fii in the PortfolioAssetSerializer, That is what I tried without success.
I get this error message: Cannot find 'setor_fii' on PortfolioAsset object, 'setor_fii' is an invalid parameter to prefetch_related()
Would like some help to achieve that.
The serializer:
class PortfolioAssetSerializer(serializers.ModelSerializer):
category = serializers.CharField(source='asset.category.name')
setor_fii = serializers.CharField(source='asset.setor_fii.name')
class Meta:
model = models.PortfolioAsset
fields = (
'id',
'category',
'setor_fii'
)
The view
class PortfolioAssetList(generics.ListAPIView):
serializer_class = serializers.PortfolioAssetSerializer
def get_queryset(self):
return models.PortfolioAsset.objects.filter(portfolio_id=self.kwargs['pk']).prefetch_related('setor_fii')
To prefetch setor_fii, you will have to go through asset. Since Fii inherits from Asset, Asset will have an automatically created one-to-one field named fii. You can then use that to access setor_fii:
PortfolioAsset.objects.filter(
portfolio_id=self.kwargs['pk'],
).prefetch_related(
'asset__fii__setor_fii',
)
Also since the whole relationships here are just one to ones, you can use select_related instead of prefetch_related to get them all in one query (compared to three queries using prefetch_related):
PortfolioAsset.objects.filter(
portfolio_id=self.kwargs['pk'],
).select_related(
'asset__fii__setor_fii',
)

django select records from multiple tables with same foreign key

I would like to execute a single query in Django which retrieves related data, by foreign key, in multiple tables. At present I have to run a query on each table e.g. (House, Furniture, People) using the House number as a filter.
In SQL I can do this in one query like this:
SELECT house.number, house.number_of_rooms, furniture.type, people.name
FROM (house INNER JOIN furniture ON house.number = furniture.house_number)
INNER JOIN people ON house.number = people.house_number
WHERE (((house.number)="21"));
Can this be done in Django?
See example models below:
class House(models.Model):
number = models.CharField('House Number', max_length=10, blank=True, unique=True, primary_key=True)
number_of_rooms = models.IntegerField(default=1, null=True)
class Furniture(models.Model):
house_number = models.ForeignKey(House, on_delete=models.CASCADE, null=True)
type = models.CharField('Furniture Type', max_length=50)
class People(models.Model):
house_number = models.ForeignKey(House, on_delete=models.CASCADE, null=True)
first_name = models.CharField('First Name', max_length=50)
In your models add related_name arguments for foreign keys, so that you can retrieve the objects related to the House() instance.
class Furniture(models.Model):
house_number = models.ForeignKey(House, related_name='house_furniture', on_delete=models.CASCADE, null=True)
type = models.CharField('Furniture Type', max_length=50)
class People(models.Model):
house_number = models.ForeignKey(House, related_name='house_people', on_delete=models.CASCADE, null=True)
first_name = models.CharField('First Name', max_length=50)
Then run the migration using following commands.
python manage.py makemigrations
python manage.py migrate
Then create a new serializers.py module in the same app.
#import models Furniture, People, house
from rest_framework import serializers
class FurnitureSerializer(serializer.ModelSerializer):
class Meta:
model = Furniture
fields = ['type'] # if you want all the fields of model than user '__all__'.
class PeopleSerializer(serializer.ModelSerializer):
class Meta:
model = People
fields = ['first_name'] # if you want all the fields of model than user '__all__'.
class HouseSerializer(serializer.ModelSerializer):
house_furniture = FurnitureSerializer(many=True)
house_people = PeopleSerializer(many=True)
class Meta:
model = Furniture
fields = ['number', 'number_of_rooms', 'house_furniture', 'house_people']
Now, in your views.py you can simply query on model House and serializer the result with HouseSerializer().
#import models from models.py
#import serializer from serializers.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.generics import ListAPIView
class ListHouseView(ListAPIView):
serializer_class = HouseSerializer
queryset = House.objects.filter() #here you can apply filters on the fields of house model and user using related_name you can filter on other related models as well.
Now, simply call ad this in your app's urls.py
url_pattern = [
path('list-house/', ListHouseView.as_view()),
]
Make sure that have a path in your project's urls.py to reach this app's urls.py.
The usual Django way of dealing with this is Queryset.prefetch_related() and iterating through Python (unless you're using Postgres, which has its own solution of ArrayAgg). Given your models, it'll cost three queries, but you won't have to deal with de-normalized row results.
h = House.objects.prefetch_related('furniture_set', 'people_set').get(number='21')
for furniture in house.furniture_set.all():
print(furniture)
for person in house.people_set.all():
print(people)
prefetch_related() caches the results and does the "joining" in Python once the queryset is evaluated, so iterating through the reverse relationships won't incur additional queries, and you're free to structure/serialize the data however you like. The raw SQL from this is something like:
SELECT house.number, house.number_of_rooms FROM house WHERE house.number = '1'
SELECT furniture.id, furniture.house_number_id, furniture.type FROM furniture WHERE furniture.house_number_id IN ('1')
SELECT people.id, people.house_number_id, people.first_name FROM people WHERE people.house_number_id IN ('1')
But Django does that behind-the-scenes so that you can just deal with a model instance in Python.

Django model order the queryset based on booleanfield True/False that related with User FK profile

I have two django models as follows:
The first one is a user profile, which has a FK to User model:
class Profile(models.Model):
PRF_user = models.OneToOneField(User, related_name='related_PRF_user', on_delete=models.CASCADE)
PRF_Priority_Support = models.BooleanField(default=False)
and the second is ticket model which has a FK to User model:
class ticket(models.Model):
ticket_status_options = [
('open', 'open'),
('wait_customer_reply', 'wait_customer_reply'),
('replied_by_staff', 'replied_by_staff'),
('replied_by_customer', 'replied_by_customer'),
('solved', 'solved'),
]
TKT_USER = models.ForeignKey(User, related_name='TKT_USER', on_delete=models.CASCADE)
TKT_DEB = models.ForeignKey('Site_departments', related_name='related_ticket_department', on_delete=models.CASCADE)
TKT_SUB = models.CharField(max_length=50, db_index=True, verbose_name="ticket subject")
TKT_BOD = models.TextField(verbose_name="ticket body")
TKT_image_attachment = models.ImageField(upload_to='TKT_img_attachment', blank=True, null=True , default=None)
TKT_CREATED_DATE = models.DateTimeField(auto_now_add=True)
TKT_UPDATED_DATE = models.DateTimeField(auto_now=True)
I want to sort the tickets based on user profile Priority_Support:
If the user profile PRF_Priority_Support is True, I want to sort it first inside my views QuerySet, otherwise (if PRF_Priority_Support is False) I want to sort it normally.
How can I do this?
You should name your model starting with a capital letter.
And for ordering the tickets, you can use something like this:
' queryset_list = ticket.objects.order_by('-TKT_USER__related_PRF_user__PRF_Priority_Support')
In filtering, when you want to span relationships, you use double underscore __ .
More on this here:
https://docs.djangoproject.com/en/3.1/topics/db/queries/#lookups-that-span-relationships
Another way is adding ordering to your model's Meta class.
For Example:
MyModel(models.Model):
class Meta:
ordering = ('-my_boolean_field ',)
Hi you should filter as follow:
Model.objects.filter(field=True) or False depending on what you need
Regards

What is the process that you follow to create model in Django?

What is the process that you follow to create model in Django? Thanks.
The most important part of a model – and the only required part of a model – is the list of database fields it defines. Fields are specified by class attributes. Be careful not to choose field names that conflict with the models API like clean, save, or delete.
Models.py
from django.db import models
class Musician(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
instrument = models.CharField(max_length=100)
class Album(models.Model):
artist = models.ForeignKey(Musician, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
release_date = models.DateField()
num_stars = models.IntegerField()
You can start here Documentation
See also Django Girls Models

django admin list_display SQL joindata

The pages made of existing php are being changed to python and Django.
Existing Query
Select
l.lawyer_idx,
l.lawyer_email,
lp.lawyer_profile_path,
lp.lawyer_profile_name,
lc.lawyer_company_name,
lc.lawyer_company_address,
lc.lawyer_detail_address,
l.lawyer_agent
from lawyer l
left join lawyer_profile lp on l.lawyer_idx = lp.lawyer_idx
left join lawyer_company lc on l.lawyer_idx = lc.lawyer_idx
order by l.register_date desc;
I made each table at models.py
models.py
class Lawyer(models.Model):
lawyer_idx = models.AutoField('ID', primary_key=True)
lawyer_email = models.CharField('E-mail', unique=True, max_length=200)
lawyer_agent = models.CharField(max_length=1, blank=True, null=True)
class Meta:
managed = False
db_table = 'lawyer'
class LawyerProfile(models.Model):
lawyer_idx = models.AutoField('ID', primary_key=True)
lawyer_profile_path = models.CharField(max_length=200, blank=True, null=True)
lawyer_profile_name = models.CharField(max_length=100, blank=True, null=True)
.................
class LawyerCompany(models.Model):
lawyer_idx = models.AutoField('ID', primary_key=True)
lawyer_company_name = models.CharField(max_length=100)
...............
We would like to place the following query into the
list_display portion of Django Admin.py Is there any way to show the data that did join in sql?
Admin.py
from django.contrib import admin
from .models import Lawyer, LawyerCompany, LawyerProfile
#admin.register(Lawyer)
class LawyerAdmin(admin.ModelAdmin):
list_per_page = 100
**list_display = ['lawyer_idx', 'lawyer_email',
'lawyer_agent', 'lawyer_profile_path', 'lawyer_profile_name', 'lawyer_company_name']**
You could add the query as a raw sql query, but that wouldnt make much use of Django's ORM then.
You are not explicitly defining any relationships on your models, so Django doesn't know about how your models relate.
If lawyer_idx references the lawyer you could change the field to a OneToOneField/ForeignKey (an AutoField is probably the wrong choice here anyways, as the values should correspond to those in the Lawyer model and not be auto-generated). Also have a look at the documentation for one-to-one and many-to-one relationships.
class LawyerProfile(models.Model):
lawyer = models.OneToOneField(Lawyer, primary_key=True,
db_column="lawyer_idx", related_name="profile")
Django should perform the joins automatically when accessing the related data; on a Lawyer instance you can then access the profile via its related_name .profile. In the list_display option you can use the double underscore syntax to access related data:
list_display = ['lawyer_idx','lawyer_agent', 'profile__lawyer_profile_path']
list_select_related = ['profile']
If you add the list_select_related option Django will already join the specified table beforehand, so no additional queries are performed when accessing the related data.