Django - get object based on ForeignKey field - django

I need to get a single model object based on a set of fields one of which is a ForeignKey. So if I have the following models:
class Composer(models.Model):
name = models.CharField()
dates = models.CharField()
nationality = models.CharField()
period = models.CharField()
class Symphony(models.Model):
composer = models.ForeignKey('Composer', related_name="symphonies", null=True)
date = models.CharField()
key = models.CharField()
number = models.IntegerField()
num_movements = models.IntegerField()
how can I then retrieve a specific Symphony object based on its composer and number fields? I originally had composer as a simple models.CharField(), so I could just do:
Symphony.objects.get(composer='Bruckner', number=7)
So how do I do the equivalent using a ForeignKey?

This should work, but you should always use a unique value:
Symphony.objects.get(composer__name='Bruckner', number=7)
Here you can find more usages for double underscore notation (__)

Related

Django add a field to a model based on another model

I have a model for a product:
class Product(models.Model):
name = models.CharField(verbose_name=_("Name"), max_length=120)
slug = AutoSlugField(populate_from="name", verbose_name=_("Slug"), always_update=False, unique=True)
I want to have a separate model ProductFields:
class ProductFields(models.Model):
field_name = models.CharField()
field_type = models.CharField()
field_verbose_name = models.CharField()
field_max_length = models.IntegerField()
filed_null = models.CharField()
field_blank = models.BooleanField()
field_default = models.CharField()
...
So the idea is whenever I add new ProductField I want Product model to migrate that added field to its database.
For Example:
ProductFields.objects.create(field_name='description', field_type='CharField', field_verbose_name='Description', field_max_length=255, filed_null=True, filed_blank=True)
This should transform Product modal to:
class Product(models.Model):
name = models.CharField(verbose_name=_("Name"), max_length=120)
slug = AutoSlugField(populate_from="name", verbose_name=_("Slug"), always_update=False, unique=True)
description = models.CharField(verbose_name="Description", max_length= 255, null=True, blank=True)
Please let me know if you have any idea how this can be done?
If you're looking for a way to create a dynamic model you can look into these suggestions.
HStoreField using django-hstore : https://django-hstore.readthedocs.io/en/latest/
JSONField: JSONField is similar to HStoreField, and may perform better with large dictionaries. It also supports types other than strings, such as integers, booleans and nested dictionaries.https://django-pgfields.readthedocs.io/en/latest/fields.html#json-field
Or you can use a NoSQL database (Django MangoDB or another adaptation)

How to avoid repetition in Django model fields?

I have models that share many common fields. For example:
class Customer(models.Model):
name = models.CharField()
email = models.CharField()
address = models.CharField()
phone = models.CharField()
city = models.CharField()
state = models.CharField()
country = models.CharField()
wallet = models.FloatField()
class Seller(models.Model):
# same fields from Customer class, except the field wallet
To avoid repeating these fields, I have tried to create classes with these common fields and link using OneToOneField:
class ContactInformation(models.Model):
phone = models.CharField()
email = models.CharField()
class AddressInformation(models.Model):
address = models.CharField()
city = models.CharField()
state = models.CharField()
country = models.CharField()
class Customer(models.Model):
wallet = models.FloatField()
contact_information = models.OneToOneField(ContactInformation)
address_information = models.OneToOneField(AddresssInformation)
class Seller(models.Model):
contact_information = models.OneToOneField(ContactInformation)
address_information = models.OneToOneField(AddresssInformation)
But now it gets very messy if I try to create a ModelForm based on the Customer, as there is only the wallet field in it. To display my other OneToOneFields I have to create multiple forms: a form for the contact information and another for address information, as ModelForms don't simply display these OneToOneFields as a single form. The views get bloated, as I have to validate 3 forms in total and have to manually create the object instances.
Am I missing something here? Should I use inheritance instead? Should I just repeat these fields to have simpler forms and views? Any advice would be greatly appreciated.
Take a look at abstract base classes, it provides a clean way to reuse common fields to multiple tables.
You might consider:
from django.db import models
class CommonUserInfo(models.model)
name = models.CharField()
email = models.CharField()
address = models.CharField()
phone = models.CharField()
city = models.CharField()
state = models.CharField()
country = models.CharField()
class Meta:
abstract = True
class Customer(CommonUserInfo):
wallet = models.FloatField()
class Seller(CommonUserInfo):
pass
I am not sure what the benefit of using a foreign key for address information is unless you have multiple customers/sellers using the same address and the addresses will need to be updated in unison.

Django querysets - Performing subqueries

How to make query to get all components added by a certain user? Also in the resulting object I want to have the system name and project name too. I saw examples demonstrating sub-queries for two tables, but I need this for 3 tables.
Thank you.
class component(models.Model):
id = models.AutoField(primary_key=True)
server = models.ForeignKey(server, on_delete="CASCADE")
name = models.TextField(blank=True)
comments = models.TextField()
timestamp = models.DateTimeField(auto_now=True)
class system(models.Model):
id = models.AutoField(primary_key=True)
project = models.ForeignKey(project, on_delete="CASCADE")
name = models.CharField(max_length=255,blank=True)
comments = models.TextField()
timestamp = models.DateTimeField(auto_now=True)
class project(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255,blank=True)
comments = models.TextField()
user = models.ForeignKey(User, on_delete="CASCADE")
timestamp = models.DateTimeField(auto_now=True)
No need to use subqueries here, we can simply perform joins, by looking "through" foreign keys (this is typically denoted with two consecutive underscores __ in Django).
If you want So all the Components that belong to a System that belongs to a Project that belongs to a user, we can query this with:
from django.db.models import F
Component.objects.filter(
system__project__user=someuser
).annotate(
system_name=F('system__name'),
project_name=F('system__project__name')
)
with someuser the user you want to filter with.
The components in this queryset will have extra attributes system_name and project_name that contain the name of the system and the project respectively.

Can't figure out join statement in Django

I'm trying to figure out how to execute the following sql join statement in Django without resorting to just raw sql. Is there a way to do it?
Select * from playertable, seasontable where player.id = season.player_id
Here are my models. Just to clarify, I used abbreviated table names in the above query for clarify
class Player(models.Model):
name = models.CharField(max_length=200)
team = models.CharField(max_length=3)
position = models.CharField(max_length=3)
class PlayerSeason(models.Model):
player = models.ForeignKey(Player)
year = models.IntegerField()
games_played = models.IntegerField()
goals = models.IntegerField()
assists = models.IntegerField()
points = models.IntegerField()
plusminus = models.CharField(max_length=200)
pim = models.IntegerField()
ppg = models.IntegerField()
shg = models.IntegerField()
gwg = models.IntegerField()
otg = models.IntegerField()
shots = models.IntegerField()
shooting_percentage = models.DecimalField(max_digits=5, decimal_places=2)
toi = models.CharField(max_length=200)
sftg = models.DecimalField(max_digits=5, decimal_places=2)
face_off = models.DecimalField(max_digits=5, decimal_places=2)
How should I do this with a Django QuerySet?
If all you wanted to do was to get all the players associated with a given season you could make use of Django's backwards relationships
When you use a ForeignKeyField to a model, in this case Season, the that model instances get an attribute which allows you to get a queryset of all the related objects.
In your example you could use season.player_set.all().
You can pass an optional parameter related_name to the ForeignKeyField that allows you to change the name of the season attribute.
Is there a way to do it?
No. Django's ORM deals with one model at a time, and you are getting columns from two tables. Perform a query on either of the models and then access the appropriate field to get the related model.

Django models - Quantity field

I'm using Django to create a part inventory where I work. Here is a snippet of the models I have:
class Part(models.Model):
description = models.CharField(max_length=64)
number= models.CharField(max_length=64)
price= models.FloatField()
class Group(models.Model):
name = models.CharField(max_length=64)
parts = models.ManyToManyField(Part)
So I have different groups (orders) with some parts in it.
What I want to do is to have a quantity property for the parts of my group. But if I want to add the quantity field to my Part object, each group will have the same quantity, which is not the correct behavior. How can I have my groups remember how much of each part they have?
Thanks for your input and I hope this is not a total noob question!
You would need a through table:
The intermediate model is associated with the ManyToManyField using the through argument to point to the model that will act as an intermediary.
class Part(models.Model):
description = models.CharField(max_length=64)
number= models.CharField(max_length=64)
price= models.FloatField()
class Group(models.Model):
name = models.CharField(max_length=64)
parts = models.ManyToManyField(Part, through='GroupPart')
Create a new through table between Group and Part that holds the quantity.