Django ForeignKey Relationship with an intermediate Table - python

I have the Goal Model and every Goal can have one Mastergoal and or multiple Subgoals.
Goals are connected with Links and every Link can have a different weight.
How do I achieve this with Django?
What I've got so far:
class Goal(models.Model):
user = models.ForeignKey(CustomUser, related_name='goals', on_delete=models.CASCADE)
name = models.CharField(max_length=300)
sub_goals = models.ManyToManyField(to='self', through='Link', symmetrical=False, related_name='master_goals')
class Link(models.Model):
master_goal = models.ForeignKey(Goal, on_delete=models.CASCADE, related_name="sub_links")
sub_goal = models.OneToOneField(Goal, on_delete=models.CASCADE, related_name="master_links")
weight = models.PositiveSmallIntegerField(default=1)
I can't just add an ForeignKey Field on Goal to itself, because every Link can have a different weight.
The solution I've got now works, but it feels wrong. I want to be able to access a Goal's Mastergoal like this: goal.master_goal

Related

How to add more columns to pivot table in many to many relations in Django?

I have 2 models united by a Many to Many relationship, in this case is Policies and Coverages so far it works fine, but I want to add another column to the pivot table since it wont work on any of the modeled tables (I want to add a IntegerField with the name 'amount' so I can store how much money are we covering for that specific coverage in that specific Insurance Police)
class Policy(models.Model):
"""Insurance Policies Model"""
number = models.CharField(max_length=25, unique=True)
company = models.OneToOneField(Company, on_delete=models.CASCADE)
client = models.OneToOneField(Client, on_delete=models.CASCADE)
start_date = models.DateField()
end_date = models.DateField()
comission = models.PositiveIntegerField()
salesperson = models.ForeignKey(Salesperson, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
rif = models.CharField(max_length=50)
phone = models.CharField(max_length=100)
class Meta:
db_table = 'policies'
class Coverage(models.Model):
"""Coverage of the different types of policies"""
name = models.CharField(max_length=55)
policies = models.ManyToManyField(Policy)
class Meta:
db_table = 'coverages'
I made the migrations and the pivot table was made without problems, but I don't know how to add another field to the pivot table
You can specify a through=... parameter [Django-doc] for that. In fact if you do not specify this yourself, Django will automatically make an "implicit model". For example:
class Policy(models.Model):
# ...
class Coverage(models.Model):
# ...
policies = models.ManyToManyField(Policy, through='CoveragePolicy')
class CoveragePolicy(models.Model):
policy = models.ForeignKey(Policy, on_delete=models.CASCADE)
coverage = models.ForeignKey(Coverage, on_delete=models.CASCADE)
amount = models.IntegerField()
So now you can create a relation object between a policy p1 and a coverage c2 with:
CoveragePolicy.objects.create(policy=p1, coverage=c2, amount=1)
You can obtain the related set of CoveragePolicy objects for a policy p1 with:
p1.coveragepolicy_set.all()
and then inspect the amount and the related coverage it has.
Django however will have troubles migrating from an existing through model to another one, so you probably will have to remove the migration file, and create a fresh one with the new model instead (and undo the changes in the database of the old one).

Using a django model as a field for another model?

I have been tasked with creating Django Models for a hypothetical apartment booking application.
My question is: can I use a model that I've defined, as a field in another model?
For example, I will have one model called "Listing" that represents an apartment being listed.
class Listing(models.Model):
address = models.IntegerField()
owner = models.CharField(max_length=256)
duration = models.DurationField()
price= models.IntegerField()
I also want to have a "Booking" model that represents an apartment once someone has booked it. It will have the exact same info as a Listing, with the addition of the username of the person who booked it. So can I have my Booking model use Listing as a field? And then just have one extra field for the booker's username.
Any other tips/critiques are highly appreciated as I am a complete beginner at Django.
I'm not 100% sure what you mean by use Listing as a field
But to me, you should be looking at the different built-in model relationships that exist in Django.
In your particular case, you will probably want to use a One-to-One relationship like so,
class Listing(models.Model):
address = models.IntegerField()
owner = models.CharField(max_length=256)
duration = models.DurationField()
price= models.IntegerField()
class Booking(models.Model):
listing= models.OneToOneField(
Listing,
on_delete=models.CASCADE,
)
username = models.Charfield()
Now if a user can book more than one apartment at a time, you'll be interested in a ForeignKey relationship like so,
class Listing(models.Model):
address = models.IntegerField()
owner = models.CharField(max_length=256)
duration = models.DurationField()
price= models.IntegerField()
class Booking(models.Model):
listing= models.ForeignKey(
Listing,
on_delete=models.CASCADE,
)
username = models.Charfield()
Note that in both examples I used Charfield for the username, feel free to use whatever Django field you need.
The concept of a model as field is odd. What you can do is establish relationships between models, or to inherit one from the other. Given your situation, you can maybe inherit Booking from Listing:
The docs on this topic.
You'll have something like this:
class Listing(models.Model):
address = models.IntegerField()
owner = models.CharField(max_length=256)
duration = models.DurationField()
price= models.IntegerField()
class Booking(Listing):
#your new fields

When you have two models connected by a foreignkey in django do you need to do a join?

The Django docs are not plainly stating this and its getting annoying.
Observe the following models:
# models
class Venue(models.Model):
name = models.CharField(max_length=150, blank=False)
description = models.CharField(max_length=1000)
image = models.ImageField(upload_to=imgUnique('venueMedia/venueImages'))
streetAddress= models.CharField(max_length=100)
city = models.CharField(max_length=100, blank=False)
state = models.CharField(max_length=100, blank=False)
class Room(models.Model):
name = models.CharField(max_length=150, blank=False)
venue = models.ForeignKey(Venue, related_name='rooms', on_delete=models.CASCADE)
description = models.CharField(max_length=1000)
standingCapacity = models.IntegerField
seatedCapacity = models.IntegerField
image = models.ImageField(upload_to=imgUnique('venueMedia/venueImages'))
I have a foreign key relationship into Venue in the Room model in the rooms venue property.
Do I need to join those tables? Or will a Venue.objects.all() do that for me automatically? If not I have the raw sql as follows will this work:
venueList = Venue.objects.raw('''SELECT *
FROM venue_Venue
INNER JOIN venue_Room
ON venue_Venue_id=venue_Room_id''')
if not that then I understand there is a select_related() method and my understanding is that I would do the following:
Venue.objects.all().select_related('Room')
Can we get this clear? As you can see I have done alot of research but am still confused
select_related
select_related can be used when you want to select the objects connected by ForeignKey or OneToOne field, more precisely you can use this only when you want to select a single connected object. select_related takes a param can use it to lookup for related_field. The param related_name='rooms' specified in your model attributes is responsible for setting related names. select_related method searches for these related names and if it finds a match then it returns you the related objects. This line
Venue.objects.all().select_related('Room')
would result in a lookup error as you don't have any related names 'Room' but you have room so you also need to watch out for case sensitivity. Below one would work.
Venue.objects.all().select_related('room')
As mentioned above select_related can only be used for selecting single related objects. If you wish to fetch multiple related objects like for ManyToManyField then use prefetch_related

Django Extend Model with Optional Fields

I have a django model that looks something like
class Person(models.Model):
name = models.CharField(max_length=100)
favorite_color = models.CharField(max_length=100)
favorite_candy = models.CharField(max_length=100)
and I want to make a template model for it. Basically, I want a model that can have an arbitrary amount of Person's fields filled out. For instance, say I wanted to have a template for Person that likes chocolate - I'd say something like chocolate_template = PersonTemplate(favorite_color='chocolate') and if I wanted someone whose name is Gerald, I could say gerald_template = PersonTemplate(name='Gerald'). The thought is that I could use these template objects to later pre-fill a Person creation form.
My main question is an implementation question. It's totally possible for me to make a template like so
class PersonTemplate(models.Model):
name = models.CharField(max_length=100, blank=True)
favorite_color = models.CharField(max_length=100, blank=True)
favorite_candy = models.CharField(max_length=100, blank=True)
but the code is horrible in that I have to manually copy-paste the contents of the Person class. This means that if I change Person, I have to remember to update PersonTemplate. I was wondering if there's a prettier way to make a 'child' of a model where all of the fields are optional. Setting all of the fields of Person to blank=True and adding an isTemplate field is also a no-no because Person and PersonTemplate should be two different entities in the database. Any thoughts on this?
Yes of course :)
class PersonTemplate(Person):
field = models.CharField(max_length=100, blank=True)
Its mean you have every fields from Person and you can add more specific fields for PersonTemplate
class Person(models.Model):
Already extend from Model, its why you have access to created, modified, id, pk....
What is good its PersonTemplate 'extend' Person who 'extend' Model.
Since Django 1.10 you can overrride field like that :
class PersonTemplate(Person):
favorite_color = models.CharField(max_length=100, blank=True)
favorite_candy = models.CharField(max_length=100, blank=True)

Customize related field in Django model specifying foreign keys to relate through

In my Django project I have a model for products that look like this:
class Manufacturer(models.Model):
name = models.CharField(max_length=100)
class Product(models.Model):
manufacturer = models.ForeignKey('Manufacturer')
# .favorite_set: ManyToOne relation coming from the
# 'Favorite' class (shown a couple of lines below)
My site's User(s) can mark some products as Favorite. To provide this functionality, I have a Django model that looks like this:
class Favorite(models.Model):
user = models.ForeignKey(User)
product = models.ForeignKey('Product')
class Meta:
unique_together = ('user', 'product',)
In that model, the .product ForeignKey creates a reverse relation in the Product model called favorite_set. That's all good and useful: When I get an HTTP request from a user to retrieve products, I can easily figure out whether it's been favorited by a particular user or not by doing this:
product = Product.objects.get(id='whatever_id')
is_favorited = bool(product.favorite_set.filter(user=self.user).count() == 1)
# or probably:
# is_favorited = product.favorite_set.filter(user=self.user).exists()
#
Now, I have another model that is heavily denormalized (SQL denormalized, that is) that I want to use for fast text searches.
This model "pretends" to be a Product, but includes data found through the "regular" Product's FK relationships into the model itself. Something like this:
class ProductSearch(models.Model):
product = models.OneToOneField('Product',
on_delete=models.CASCADE,
related_name='searcher')
product_name = models.CharField(max_length=100)
manufacturer_name = models.CharField(max_length=100)
This class has its own id field (since it's a Django model) and, as you can see above, it is going to have a OneToOne relationship to the products (one of this ProductSearch entries is linked to one and only one Product)
Thanks to this model, if I want to search products whose manufacturer is "Ford" (for example), I don't need to join the Product table with the Manufacturer's table. I can do the lookup directly in ProductSearch and save a few milliseconds.
Since the ProductSearch is intended to be compatible with a Product, I'm also trying to model the favorite_set that occurs "naturally" in my Product class into this ProductSearch model.
And that's where the difficulties arise: I don't know how to do that :-)
I ideally would have something like:
class ProductSearch(models.Model):
product = models.OneToOneField('Product',
on_delete=models.CASCADE,
related_name='searcher')
manufacturer_name = models.CharField(max_length=100)
#
# Couldn't find anything to do the following:
product_favorite_set = models.ManyToOneField('Favorite',
through_fields=('product',))
But I haven't been able to do that.
I have tried to "abuse" the ManyToManyField like this:
class ProductSearch(BaseModel):
product = models.OneToOneField('Product',
on_delete=models.CASCADE,
related_name='searcher')
product_name = models.CharField(max_length=100)
manufacturer_name = models.CharField(max_length=100)
product_favorite_set = models.ManyToManyField('Favorite', related_name='+',
through='Favorite',
through_fields=['product']
)
But that produces an error on System Check:
api.Favorite: (fields.E336) The model is used as an intermediate model
by 'api.ProductSearch.product_favorite_set', but it
does not have a foreign key to 'ProductSearch' or 'Favorite'.
api.ProductSearch.product_favorite_set: (fields.E339) 'Favorite.product'
is not a foreign key to 'ProductSearch'.
I imagine I could make the product_favorite_set a Python #property, and then do a custom query in it like:
class ProductSearch(BaseModel):
# ....
#property
def product_favorite_set(self):
return Favorite.objects.filter(product=self.product)
But I would like to know if I can do this using "pure" Django tools (only if out of curiosity)
Any help would be greatly appreciated. Thank you in advance.

Categories