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.
Related
I am new to Django and was looking for an efficient way to retrieve and post to a ManytoMany relation using an intemrediary table.
models:
from django.db import models
# Create your models here.
class Actor(models.Model):
name = models.CharField(max_length = 50, primary_key = True)
bio = models.CharField(max_length=150)
class Movie(models.Model):
name = models.CharField(max_length=100)
release = models.CharField(max_length=100)
class NormActors(models.Model):
movie = models.ForeignKey(Movie, on_delete=models.CASCADE)
actor = models.ForeignKey(Actor, on_delete=models.CASCADE)
I have checked the documentation and it got confusing, so any reference would be helpful as well. If on request, I want to send -
for /movies: get list of all movies + details with the actor names in the respective movies
for /actors: get list of all actors + details with the movies they have been in
Should the ORM query look something like this?
actor = Actor.objects.get(name="XYZ")
movies = actor.movie_set.all()
How should I go about fr the first one?
Firstly, you should explicitly declare the m2m and identify the through table as such:
class Movie(models.Model):
...
actors = models.ManyToManyField('Actor', through='NormActor')
Now for any movie you can do movie.actors.all(), and for any actor you can do actor.movie_set.all().
Note, if you only have those two fields on NormActor, you don't actually need to declare it explicitly; you can remove that model and the through attribute and Django will manage it for you, with the added bonus that in the admin interface you can now edit actors inline with movies.
To help me avoid confusion, I always call a "through" table by the name of 2 other tables. Thus, in your case, I would have done:
class Actor(models.Model):
name = models.CharField(max_length = 50, primary_key = True)
bio = models.CharField(max_length=150)
movies = models.ManyToMany('Movie', through='ActorMovie',
related_name='actors')
class Movie(models.Model):
name = models.CharField(max_length=100)
release = models.CharField(max_length=100)
class ActorMovie(models.Model):
movie = models.ForeignKey(Movie, on_delete=models.CASCADE)
actor = models.ForeignKey(Actor, on_delete=models.CASCADE)
Notes:
the use of related_name is important and imagine it's the reverse direction: you declared the ManyToMany in Actor so reverse is Movie -> Actor
'Movie' in quote because it's declared after this declaration. You can remove quotes but then declare Movie class first.
same for ActorMovie
with a ManyToMany declared this way you can access in both directions (= from Actor but also from Movie) in a very clear way, for example two working and very clean queries.
Examples:
# get all film with Harrison Ford:
for movie in Actor.objects.filter(name="Harrison Ford").movies.all():
pass # do whatever with movie
# get all actors from Die Hard movie:
for actor in Movie.objects.filter(name__istartswith="Die Hard").actors.all():
pass # do whatever with actor
If you want to get all the movies related to an actor, you need to have ManyToMany relationship with Movie model with respect to Actor model.
class Movie(models.Model):
name = models.CharField(max_length=100)
release = models.CharField(max_length=100)
class Actor(models.Model):
name = models.CharField(max_length = 50, primary_key = True)
bio = models.CharField(max_length=150)
movies = models.ManyToManyField(Actor)
Remember, you need to define Movie model before you creating a manytomany relationship with the Actor model.
actor = Actor.objects.get(name="XYZ")
movies = actor.movies.all()
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
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 (__)
Looking for advice on setting up this model.
This job board app has Company, Location, and Job. They should have the following relationships:
A Company can have multiple locations
A Company can have multiple jobs
A Job can have only one Company
A Job can have multiple locations, BUT each Location must be valid for the job's Company
I'd like to create a model that reflects these relationships. I think something like this might work:
class Company(models.Model):
name = models.CharField(max_length=200)
description = models.TextField()
class Location(models.Model):
is_primary_location = models.BooleanField()
address = models.CharField(max_length=200)
company = models.ForeignKey(Company)
class Job(models.Model):
title = models.CharField(max_length=200)
company = models.ForeignKey(Company)
location = models.ForeignKey(Location)
But I would really like the "Job has Location(s) through Company" relationship to be enforced. The model doesn't enforce it; I think I'd have to filter the valid Locations when data is displayed, and I'd like to avoid that.
Thanks very much!
Take a look at ForeignKey.limit_choices_to.
This allows you to filter the available choices and is enforced in ModelForm. Since you already have the company foreign key in your Job model, you should be able to use that to filter the choices.
I ended up using https://github.com/digi604/django-smart-selects and wrote the model like this. I don't think limit_choices_to works in this case (according to other SO threads)
from smart_selects.db_fields import ChainedForeignKey
class Company(models.Model):
name = models.CharField(max_length=200)
description = models.TextField()
class Location(models.Model):
is_primary_location = models.BooleanField()
address = models.CharField(max_length=200)
company = models.ForeignKey(Company)
class Job(models.Model):
title = models.CharField(max_length=200)
company = models.ForeignKey(Company)
location = ChainedForeignKey(
Location,
chained_field="company",
chained_model_field="company",
show_all=False,
auto_choose=True
)
say, we are making a site where customers (Customer) can come and rate publishing houses (PublishingHouse). We need to store address of both Customer and PublishingHouse, so we make an Address class like this:
class Address(models.Model):
line1 = models.CharField(max_length=50)
line2 = models.CharField(max_length=50)
city = models.CharField(max_length=50)
postal_code = models.CharField(max_length=6)
country = models.CharField(max_length=50)
and then our Customer and PublishingHouse class have references to it, like this:
class Customer(models.Model):
name = models.CharField(max_length=50)
address = models.ForeignKey(Address)
class PublishingHouse(models.Model):
name = models.CharField(max_length=50)
website = models.URLField(max_length=4096)
address = models.ForeignKey(Address)
this works fine for my interactive sessions in django shell, but when I activate admin for these models, is there a way that I could make fields from Address appear on Add Customer or Add Publishing House page?
I am using django 1.5
You're probably looking for generic inlines. As per the documented example Address would be your Image model
As per your comment, you could using class inheritance:
class Address(models.Model):
# fields
class DynamicAddress(Address):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey("content_type", "object_id")
# etc
Also you can limit (generic)inlines with the max_num attribute on your generic inline admin class.