ManytoManyField Django : how to call models in methods? - python-3.x

I need help for something, I want to call models with ManyToManyField.
I want to have method to get Class A from Class B, and another in Class B to get Class A.
here's my (shortened) code :
class Licence(models.Model):
name = models.CharField(max_length=64)
picture = models.ImageField(upload_to='finder/static/finder/img/licence/',null=True, blank=True)
description = models.TextField(null=True, blank=True)
#returns a list of games from this license
def getGamesOnThisLicence(self):
#i don't know how to proceed
class Game(models.Model):
name = models.CharField(max_length=64)
description = models.TextField()
release_date = models.DateField(null=True, blank=True)
licence = models.ManyToManyField(Licence, blank=True, null=True)
#return name of licence to which the game belongs
def getLicenceName(self):
return self.licence.name

You can access the Games with:
my_license.game_set.all()
so you can use self in the getGamesOnThisLicense, but probably there is not much gain to define a function since this makes accessing the Games already quite convenient.
Perhaps you however want to transform the ManyToManyField into a ForeignKey to License since self.license.name makes not much sense: for a ManyToManyField, self.license is a Manager over License objects that can manage zero, one or more Licenses, so you can not use self.license.name.

Related

Django ORM query with user defined fields

I'm trying to create an Django ORM query to replace a really messy raw SQL query i've written in the past but i'm not sure if Django ORM can let me do it. I have three tables:
contacts
custom_fields
custom_field_data
What I'm hoping to be able to do with the ORM is create an output as if i've queryied one single table like this:
Is such a thing possible with the ORM?
EDIT:
The models are:
class Contact(models.Model):
company = models.ForeignKey(Company, on_delete=models.PROTECT, null=True, blank=True)
class CustomField(models.Model):
name = models.CharField(max_length=100)
company = models.ForeignKey(Company, on_delete=models.PROTECT, null=False, blank=False)
class ContactCustomFieldValue(models.Model):
custom_field = models.ForeignKey(CustomField, on_delete=models.PROTECT, related_name='contact_values')
contact = models.ForeignKey(Contact, on_delete=models.PROTECT, related_name='custom_field_values', null=True)
value = models.TextField(null=True)
Solved this one in large part thanks to Ken from the Django forums.
The solution he provided looked like this:
subquery1 = Subquery(ContactCustomFieldValue.objects.filter(contact=OuterRef('id'), custom_field_id=1).values_list('value'))
subquery2 = ...
subquery3 = ...
subquery4 = ...
contact_list = Contact.objects.filter(...).annotate(field1=subquery1, ...)
I've built on it to fit my needs but as a starting point this was perfect

How to get value of ForeignKey in views.py?

I have below models.py and views.py in Django(This is only a example). How can i print the value of ForeignKey in views.py?
models.py
class student(models.Model):
name = models.CharField(max_length=50)
desc = models.CharField(max_length=100, blank=True, null=True)
phone_number = models.ForeignKey(tell,on_delete=models.SET_NULL, null = True,blank=True)
def __str__(self):
return self.name
class tell(models.Model):
phone_number = models.CharField(max_length=20, blank=True, null=True)
def __str__(self):
return self.phone_number
views.py
phones = student.objects.values('phone_number')
phone = list(phones)
for ob in phone:
print(ob)
This prints only id but i want the value of foreign key.
This is one of the (many) reasons not to use .values(…) [Django-doc]: it erodes the model logical layer of a Django model.
You can work with:
tell.objects.filter(student__isnull=False)
this will retrieve all tell ojects that have at least a related Student.
You can also use .select_related(…) [Django-doc] to select the telephone number while retrieving the students, and thus avoid an N+1 problem:
students = student.objects.select_related('phone_number'):
for stud in students:
print(stud.phone_number)
If you really want to work with the phone_number of the tell model, you can work with .values_list(…) [Django-doc], and work with:
phones = student.objects.values_list('phone_number__phone_number', flat=True)
for phone in phones:
print(phone)
but this are thus not tell objects, and thus updating, refrehing, a record is not possible in that case.

Django Foreign key to not loaded object

Hello I have a problem that I want to link foreign key to model's related object in other words I want to link foreign key to not loaded object's fields.
class Category(models.Model):
...
filter_option_content_type = models.ForeignKey(ContentType, on_delete=models.SET_NULL, limit_choices_to=(
models.Q(app_label='catalog', model='FrameSize') |
models.Q(app_label='catalog', model='ShoeSize') |
models.Q(app_label='catalog', model='ClothSize')
), null=True)
...
class Product(models.Model):
...
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
...
class ProductItem(models.Model):
...
model = models.ForeignKey(Product, verbose_name='Модель', on_delete=models.CASCADE, related_name='productitem')
size = models.ForeignKey('model.category.filter_option_content_type', on_delete=models.SET_NULL, null=True)
...
And sure i got this error:
ValueError: Invalid model reference 'model.category.filter_option_content_type'. String model references must be of the form 'app_label.ModelName'
is it possible to do relation like this wtihout using GenericForeignKey instead of ForeignKey?
I think that you don't really need to add an additional relation since you can access the field you want via Product:
item = ProductItem()
size = item.model.category.filter_option_content_type
In case you'd like to query using this field, it would look like:
item = ProductItem.objects.filter(
model__in=Product.objects.filter(
category__in=Category.objects.filter(
filter_option_content_type='<your desired value of size>'
)
)
)
Since the relations are based on Foreign Keys which are indexed - such query shouldn't have a noticeable effect on performance (despite that it may seem a bit awkward)

SystemCheckError: System check identified some issues:

I am building a project to place an order. When I run makemigrations command then it gives an error of
SystemCheckError: System check identified some issues and I have deleted the migration file from migrations
Traceback:
order.Order.price: (fields.E304) Reverse accessor for 'Order.price' clashes with reverse accessor for 'Order.product'.
HINT: Add or change a related_name argument to the definition for 'Order.price' or 'Order.product'.
order.Order.product: (fields.E304) Reverse accessor for 'Order.product' clashes with reverse accessor for
'Order.price'.
HINT: Add or change a related_name argument to the definition for 'Order.product' or 'Order.price'.
My models.py is as follows:
class Order(models.Model):
company = models.ForeignKey(Company, on_delete=models.CASCADE)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
price = models.ForeignKey(Product, on_delete=models.CASCADE)
def __str__(self):
return self.company + self.product + self.price
Add related_name like:
company = models.ForeignKey(Company, on_delete=models.CASCADE, related_name='company')
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='product')
price = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='price')
related_name is used to link the models. Especially, when you have more than 1 model linking to another model. It is compulsary as in your case. See_this_answer to understand properly about use of related_name.
Both your foreign keys referrencing same model Product without defining different related names:
product = models.ForeignKey(Product, on_delete=models.CASCADE)
price = models.ForeignKey(Product, on_delete=models.CASCADE)
If that's not an error then simply add different related names

Proper way to do a robust search in Django models via REST framework

I'm writing a web application (DRF + Vue.js) where frontend should have an ability to narrow down GET request results via different filters.
For example, I have a model like this:
class Human(models.Model):
first_name = models.CharField(_('first name'), max_length=50, null=True, blank=True)
last_name = models.CharField(_('last name'), max_length=50, null=True, blank=True)
birth_date = models.DateField(_('birth date'), blank=True, null=True)
city = models.ForeignKey('City', on_delete=models.SET_NULL, blank=True, null=True)
phone_number = models.ForeignKey('Contact' on_delete=models.SET_NULL, blank=True, null=True)
#property
def full_name(self):
# Last name + First name
return ' '.join(str(x) for x in (self.last_name, self.first_name) if x)
#property
def is_adult(self):
now = timezone.now()
if self.birth_date:
if now.year - self.birth_date.year - \
((now.month, now.day) < (self.birth_date.month, self.birth_date.day)) >= 18:
return True
return False
Now I have simple CRUD ViewSet where I can use a list of search_fields to search by all needed fields (in my case that's birth_date, city, phone_number and full_name/is_adult). But here next problems arise:
Using search_fields I can do a search only by all fields specified in the ViewSet's search_fields (frontend can't search only by city or other distinct fields if it wants to) - other way I'll need to create a separate ViewSet (and separate URL?) for every combination of fields to filter by. Terrific.
So it looks like the correct decision must be capable of filtering by several GET parameters at once. Ideally - with opportunity to choose exact/icontains/etc comparison method on each query.
That sounds like a work for django-filter but I'm not sure yet.
It's impossible to search/filter by full_name or is_adult because they are dynamic model properties, not usual fields.
So it looks like instead of using model properties I need to use separate QuerySets (Manager methods?) that will do the logic of fiddling with model fields and creating the filtered result.
But for now I didn't find a way to choose different QuerySets in a single ViewSet depending on GET parameters (or how to use search by these complex properties together with simple search from problem 1?).
And I have no understanding if it is possible to provide this kind of search by the same URL as a "simple" search - like site.com/api/people/?city=^New&full_name=John%20Doe (perfectly - with opportunity to document query parameters for OpenAPI schema / Swagger)
So maybe someone knows which is the most elegant way to provide a capability of such complex search with Django/DRF? In which direction should I look?
"full_name" alias needs to be part of your queryset.
You can achieve it by queryset annotation.
In your People view (api/people/ controller) you have to set your queryset to be:
from django.db.models.functions import Concat
from django.db.models import Value
queryset = Human.objects.annotate(fullname=Concat('first_name', Value(' '), 'last_name'))
Also, customize your "filter_backed" to act the way you need.
class PeopleFilterBackend(filters.BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
params_serializer = PeopleFilterSerializer(data=request.params)
params_serializer.is_valid(raise_exception=True)
valid_params = params_serializer.data
if full_name := valid_params.get("full_name")
queryset = queryset.filter(full_name__icountains=full_name)
# insert here all of other filters logic
return queryset
PeopleFilterSerializer is your custom params serializer,
You have to specify there all of your accepted params:
from rest_framework import serializers
class PeopleFilterSerializer(serializers.Serializer):
full_name = serializers.CharField() # set required=False if not required
city = serializer.CharField()

Resources