In general, how can I keep my models DRY when I have to repeat several attributes multiple times?
For example:
class Event(models.Model):
title = models.CharField(max_length=255)
postal_code = models.CharField(max_length=5)
city = models.CharField(max_length=50)
street = models.CharField(max_length=50)
street_nr = models.CharField(max_length=5)
class Person(models.Model):
name = models.CharField(max_length=50)
postal_code = models.CharField(max_length=5)
city = models.CharField(max_length=50)
street = models.CharField(max_length=50)
street_nr = models.CharField(max_length=5)
Normalize your database structure, you could normalize Address even further
class Adress(models.Model):
postal_code = models.CharField(max_length=5)
city = models.CharField(max_length=50)
street = models.CharField(max_length=50)
street_nr = models.CharField(max_length=5)
class Event(models.Model):
title = models.CharField(max_length=255)
address = models.ForeignKey(Adress, on_delete=models.CASCADE)
class Person(models.Model):
name = models.CharField(max_length=50)
address = models.ForeignKey(Adress, on_delete=models.CASCADE)
Just add refs:
class AdressData(models.Model):
postal_code = models.CharField(max_length=5)
city = models.CharField(max_length=50)
street = models.CharField(max_length=50)
street_nr = models.CharField(max_length=5)
class Event(models.Model):
title = models.CharField(max_length=255)
address_data = models.ForeignKey('AdressData')
class Person(models.Model):
name = models.CharField(max_length=50)
address_data = models.ForeignKey('AdressData')
Django models can use Python's object inheritance to share common data; with a provision. These are referred to as abstract models.
To refactor your code using an abstract models I would write it like so:
class BaseAddressModel(models.Model):
postal_code = models.CharField(max_length=5)
city = models.CharField(max_length=50)
street = models.CharField(max_length=50)
street_nr = models.CharField(max_length=5)
class Meta:
abstract = True
class Event(BaseAddressModel):
title = models.CharField(max_length=255)
class Person(BaseAddressModel):
name = models.CharField(max_length=50)
Previous answers might serve your purpose better, but this is an option.
Related
I realize that the 3 models that I use right now have a ton of shared fields. I was wondering what the best way to condense these models would be. I've read some articles on metaclasses and model inheritance but wanted to see what the "best" way to do this would be.
models.py
class Car(models.Model):
created = models.DateTimeField(auto_now_add=True)
make = models.CharField(max_length=100)
model = models.CharField(max_length=100)
year = models.IntegerField(default=2021, validators=[MinValueValidator(1886), MaxValueValidator(datetime.now().year)])
seats = models.PositiveSmallIntegerField()
color = models.CharField(max_length=100)
VIN = models.CharField(max_length=17, validators=[MinLengthValidator(11)])
current_mileage = models.PositiveSmallIntegerField()
service_interval = models.CharField(max_length=50)
next_service = models.CharField(max_length=50)
class Truck(models.Model):
created = models.DateTimeField(auto_now_add=True)
make = models.CharField(max_length=100)
model = models.CharField(max_length=100)
year = models.IntegerField(default=datetime.now().year, validators=[MinValueValidator(1886), MaxValueValidator(datetime.now().year)])
seats = models.PositiveSmallIntegerField()
bed_length = models.CharField(max_length=100)
color = models.CharField(max_length=100)
VIN = models.CharField(max_length=17, validators=[MinLengthValidator(11)])
current_mileage = models.PositiveSmallIntegerField()
service_interval = models.CharField(max_length=50)
next_service = models.CharField(max_length=50)
class Boat(models.Model):
created = models.DateTimeField(auto_now_add=True)
make = models.CharField(max_length=100)
model = models.CharField(max_length=100)
year = models.PositiveSmallIntegerField(default=datetime.now().year, validators=[MaxValueValidator(datetime.now().year)])
length = models.CharField(max_length=100)
width = models.CharField(max_length=100)
HIN = models.CharField(max_length=14, validators=[MinLengthValidator(12)], blank=True)
current_hours = models.PositiveSmallIntegerField()
service_interval = models.CharField(max_length=50)
next_service = models.CharField(max_length=50)
I can notice that some fields are similar but aren't the same, for example year are not the same for all. Due to that I propose to you the following.
class TimeStampedModel(models.Model):
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class Vehicule(TimeStampedModel):
make = models.CharField(max_length=100)
model = models.CharField(max_length=100)
service_interval = models.CharField(max_length=50)
next_service = models.CharField(max_length=50)
class Meta:
abstract = True
class WheeledVehicle(Vehicule):
seats = models.PositiveSmallIntegerField()
color = models.CharField(max_length=100)
VIN = models.CharField(max_length=17, validators=[MinLengthValidator(11)])
current_mileage = models.PositiveSmallIntegerField()
class Meta:
abstract = True
class Car(WheeledVehicle):
year = models.IntegerField(default=2021, validators=[MinValueValidator(1886), MaxValueValidator(datetime.now().year)])
class Truck(WheeledVehicle):
year = models.IntegerField(default=datetime.now().year, validators=[MinValueValidator(1886), MaxValueValidator(datetime.now().year)])
bed_length = models.CharField(max_length=100)
class Boat(Vehicule):
year = models.PositiveSmallIntegerField(default=datetime.now().year, validators=[MaxValueValidator(datetime.now().year)])
length = models.CharField(max_length=100)
width = models.CharField(max_length=100)
HIN = models.CharField(max_length=14, validators=[MinLengthValidator(12)], blank=True)
current_hours = models.PositiveSmallIntegerField()
And if you want to go further, I suggest you to add created_by and modified_by fields to audit.
class TimeStampedModel(models.Model):
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class TimeStampedAuthModel(TimeStampedModel):
created_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.SET_NULL,
related_name="%(app_label)s_%(class)s_created_by")
modified_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.SET_NULL,
related_name="%(app_label)s_%(class)s_modified_by")
class Meta:
abstract = True
class Vehicule(TimeStampedAuthModel):
make = models.CharField(max_length=100)
model = models.CharField(max_length=100)
service_interval = models.CharField(max_length=50)
next_service = models.CharField(max_length=50)
class Meta:
abstract = True
class WheeledVehicle(Vehicule):
seats = models.PositiveSmallIntegerField()
color = models.CharField(max_length=100)
VIN = models.CharField(max_length=17, validators=[MinLengthValidator(11)])
current_mileage = models.PositiveSmallIntegerField()
class Meta:
abstract = True
class Car(WheeledVehicle):
year = models.IntegerField(default=2021, validators=[MinValueValidator(1886), MaxValueValidator(datetime.now().year)])
class Truck(WheeledVehicle):
year = models.IntegerField(default=datetime.now().year, validators=[MinValueValidator(1886), MaxValueValidator(datetime.now().year)])
bed_length = models.CharField(max_length=100)
class Boat(Vehicule):
year = models.PositiveSmallIntegerField(default=datetime.now().year, validators=[MaxValueValidator(datetime.now().year)])
length = models.CharField(max_length=100)
width = models.CharField(max_length=100)
HIN = models.CharField(max_length=14, validators=[MinLengthValidator(12)], blank=True)
current_hours = models.PositiveSmallIntegerField()
You can use model inheritance or model Mixins
model inheritance with abstract base model:
class AbstractVehicle(models.Model):
created = models.DateTimeField(auto_now_add=True)
make = models.CharField(max_length=100)
model = models.CharField(max_length=100)
year = models.IntegerField(default=2021, validators=[MinValueValidator(1886), MaxValueValidator(datetime.now().year)])
class Meta:
abstract = True
class Car(AbstractVehicle):
seats = models.PositiveSmallIntegerField()
color = models.CharField(max_length=100)
VIN = models.CharField(max_length=17, validators=[MinLengthValidator(11)])
current_mileage = models.PositiveSmallIntegerField()
service_interval = models.CharField(max_length=50)
next_service = models.CharField(max_length=50)
class Truck(AbstractVehicle):
seats = models.PositiveSmallIntegerField()
bed_length = models.CharField(max_length=100)
color = models.CharField(max_length=100)
VIN = models.CharField(max_length=17, validators=[MinLengthValidator(11)])
current_mileage = models.PositiveSmallIntegerField()
service_interval = models.CharField(max_length=50)
next_service = models.CharField(max_length=50)
class Boat(AbstractVehicle):
length = models.CharField(max_length=100)
width = models.CharField(max_length=100)
HIN = models.CharField(max_length=14, validators=[MinLengthValidator(12)], blank=True)
current_hours = models.PositiveSmallIntegerField()
service_interval = models.CharField(max_length=50)
next_service = models.CharField(max_length=50)
model mixins:
class VehicleMixin(object):
created = models.DateTimeField(auto_now_add=True)
make = models.CharField(max_length=100)
model = models.CharField(max_length=100)
year = models.IntegerField(default=2021, validators=[MinValueValidator(1886), MaxValueValidator(datetime.now().year)])
class Car(VehicleMixin, models.Model):
seats = models.PositiveSmallIntegerField()
color = models.CharField(max_length=100)
VIN = models.CharField(max_length=17, validators=[MinLengthValidator(11)])
current_mileage = models.PositiveSmallIntegerField()
service_interval = models.CharField(max_length=50)
next_service = models.CharField(max_length=50)
class Truck(VehicleMixin, models.Model):
seats = models.PositiveSmallIntegerField()
bed_length = models.CharField(max_length=100)
color = models.CharField(max_length=100)
VIN = models.CharField(max_length=17, validators=[MinLengthValidator(11)])
current_mileage = models.PositiveSmallIntegerField()
service_interval = models.CharField(max_length=50)
next_service = models.CharField(max_length=50)
class Boat(VehicleMixin, models.Model):
length = models.CharField(max_length=100)
width = models.CharField(max_length=100)
HIN = models.CharField(max_length=14, validators=[MinLengthValidator(12)], blank=True)
current_hours = models.PositiveSmallIntegerField()
service_interval = models.CharField(max_length=50)
next_service = models.CharField(max_length=50)
Both of these solutions will leave you with the same tables as your code. Also these are just simple examples. You may be able to improve them further. For Example you could add even more mixins or abstract base classes that contain the other fields.
You can combine the model by creating Vehicle and VehicleType in vehicle model you keep all common vehicle fields, in VehicleType keep the car, truck,boat etc with foreign-key reference to Vehicle model.
class VehicleType(models.Model):
name = models.CharField(max_length=100, unique=True)
created = models.DateTimeField(auto_now_add=True)
class Vehicle(models.Model):
vehicle_type = models.Foreinkey(VehicleType, on_delete=model.CASCADE)
created = models.DateTimeField(auto_now_add=True)
make = models.CharField(max_length=100)
model = models.CharField(max_length=100)
year = models.IntegerField(default=datetime.now().year, validators=[MinValueValidator(1886), MaxValueValidator(datetime.now().year)])
seats = models.PositiveSmallIntegerField()
bed_length = models.CharField(max_length=100)
color = models.CharField(max_length=100)
VIN = models.CharField(max_length=17, validators=[MinLengthValidator(11)])
current_mileage = models.PositiveSmallIntegerField()
service_interval = models.CharField(max_length=50)
next_service = models.CharField(max_length=50)
but when i run it i shows errors that it cant find the muscel_id i Muscel class, how can i show only the names. i ma trying to build a workout plan using django
class Days(models.Model):
day_name = models.CharField(max_length=60)
class Muscel(models.Model):
Muscel_name = models.CharField(max_length=60)
class Exercise(models.Model):
exercise_name = models.CharField(max_length=70)
class Basic(models.Model):
dagen_basic = models.ForeignKey(Days, related_name='days_basic', on_delete=models.CASCADE)
muskel = models.ForeignKey(Muscel.Muscel_name, related_name='muskel_basic',
on_delete=models.CASCADE)
exercise_name = models.ForeignKey(Exercise.exercise_name, related_name='exercise_name_basic',
on_delete=models.CASCADE)
reps = models.CharField(max_length=20)
sets = models.IntegerField()
try this:
class Muscel(models.Model):
muscel_name = models.CharField(max_length=60)
def __str__(self):
return self.muscel_name
add str on your Muscel model,
class Basic(models.Model):
......
muskel = models.ForeignKey(Muscel, related_name='muskel_basic',
on_delete=models.CASCADE)
exercise_name = models.ForeignKey(Exercise, related_name='exercise_name_basic',
on_delete=models.CASCADE)
......
remove .Muscel_name on ForeignKey
This works, but is there a Django idiom that does it better?
my_books = Book.objects.filter(author__name=='me')
my_publishers = Publisher.objects.filter(pk__in=[b.publisher.id for b in my_books])
models = round_up_the_usual_suspects()
class Publisher(models.Model):
name = models.CharField(max_length=30)
class Author(models.Model):
name = models.CharField(max_length=30)
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey(Author, related_name='books_authored')
publisher = models.ForeignKey(Publisher, related_name='books_published')
You can do
my_publishers = Publisher.objects.filter(book__author__name='me')
This gives you all the publishers for the book authored by me
I've written some Django models and loaded some data for a bus timetable system but am having trouble with writing a query. Here are the models:
class VehicleJourney(models.Model):
vehicle_journey = models.CharField(primary_key=True, max_length=100)
journey_pattern = models.ForeignKey('JourneyPattern')
departure_time = models.TimeField()
class TimingLink(models.Model):
timing_link = models.CharField(primary_key=True, max_length=100)
journey_pattern = models.ForeignKey('JourneyPattern')
from_seq_no = models.IntegerField()
from_stop = models.ForeignKey('Stop', related_name='+')
to_seq_no = models.IntegerField()
to_stop = models.ForeignKey('Stop', related_name='+')
runtime = models.IntegerField()
class Stop(models.Model):
stop_point = models.CharField(primary_key=True, max_length=100)
common_name = models.CharField(max_length=250)
class JourneyPattern(models.Model):
journey_pattern = models.CharField(primary_key=True, max_length=100)
direction = models.CharField(max_length=100)
service = models.ForeignKey('Service')
class Service(models.Model):
service_code = models.CharField(primary_key=True, max_length=100)
registered_operator_ref = models.ForeignKey('Operator')
mode = models.CharField(max_length=100)
description = models.CharField(max_length=250)
start_date = models.DateField()
end_date = models.DateField()
origin = models.CharField(max_length=250)
destination = models.CharField(max_length=250)
The query takes an input of from_stop, to_stop and an arrival time to find the 3 next buses departing from from_stop,and arriving at to_stop by arrival_time.
I want to return the TimingLinks between from_stop and to_stop, with each runtime successively added to VehicleJourney.departure_time giving departure times for each stop.
Any insights on how I could best approach this?
How to display in OrderAdmin: Basket owner, products, quantity of products?
I try Inline:
admin.py:
class BasketInline(admin.TabularInline):
model = Basket
class OrderAdmin(admin.ModelAdmin):
inlines = [
BasketInline,
]
admin.site.register(Order, OrderAdmin)
but it does not work.
class Basket(models.Model):
owner = models.ForeignKey(User, related_name='user_basket', verbose_name='Owner')
name = models.CharField("Basket_Name", max_length=30)
products = models.ManyToManyField('Product', through='BasketProduct', blank=True, null=True)
class BasketProduct(models.Model):
product = models.ForeignKey('Product')
basket = models.ForeignKey('Basket')
quantity = models.IntegerField()
class Product(models.Model):
name = models.CharField(max_length=50)
slug = models.SlugField()
unit_price = models.DecimalField(max_digits=5, decimal_places=2)
desc = models.TextField()
category = models.ManyToManyField(Category)
class ShippingOptions(models.Model):
name = models.CharField(max_length=50)
price = models.DecimalField(max_digits=5, decimal_places=2)
time = models.CharField(max_length=150)
class Order(models.Model):
bask = models.OneToOneField(Basket)
shipp = models.OneToOneField(ShippingOptions)
Maybe I need a different way. Please any help. Thanks
You need foreignKey on Order in you Basket model:
class Basket(models.Model):
owner = models.ForeignKey(User, related_name='user_basket', verbose_name='Owner')
name = models.CharField("Basket_Name", max_length=30)
products = models.ManyToManyField('Product', through='BasketProduct', blank=True, null=True)
owner = models.ForeignKey(Owner, blank=True, verbose_name='Owner')
or you have error in first model row:
owner = models.ForeignKey(Owner, related_name='user_basket', verbose_name='Owner')