Creating "classes" with Django - python

I'm just learning Django so feel free to correct me in any of my assumptions. I probably just need my mindset adjusted.
What I'm trying to do is creating a "class" in an OOP style. For example, let's say we're designing a bunch of Rooms. Each Room has Furniture. And each piece of Furniture has a Type and a Color. What I can see so far is that I can have
class FurnitureType(models.Model):
name = models.CharField(max_length=200)
class FurnitureColor(models.Model):
name = models.CharField(max_length=50)
class FurniturePiece(models.Model):
type = models.ForeignKey(FurnitureType)
color = models.ForeignKey(FurnitureColor)
sqft = models.IntegerField()
name = models.CharField(max_length=200)
class Room(models.Model):
name = models.CharField(max_length=200)
furnitures = models.ManyToManyField(FurniturePiece)
The problem is that each FurniturePiece has to have a unique name if I'm picking it out of the Django admin interface. If one person creates "Green Couch" then no one else can have a "Green Couch". What I'm wondering is if a) I need to learn more about Django UI and this is the right way to design this in Django or b) I have a bad design for this domain
The reason I want Furniture name to be unique is because 10 people could create a "Green Couch" each with a different sqft.

I don't get the problem with unique name. You can just specify it to be unique:
class FurniturePiece(models.Model):
type = models.ForeignKey(FurnitureType)
color = models.ForeignKey(FurnitureColor)
sqft = models.IntegerField()
name = models.CharField(max_length=200, unique=True)
I don't know whether you have to learn about Django UI or not. I guess you have to learn how to define models. The admin interface is just a generated interface based on your models. You can change the interface in certain aspects without changing the models, but besides that, there is less to learn about the admin interface.
I suggest you follow a tutorial like the djangobook, to get a good start with Django.
I think, the problem that you have is not how to use Django but more that you don't know how to model your application in general.
First you have to think about which entities do yo have (like Room, Furniture, etc.).
Then think about what relations they have.
Afterwards you can model them in Django. Of course in order to do this you have to know how to model the relations. The syntax might be Django specific but the logical relations are not. E.g. a many-to-many relation is not something Django specific, this is a term used in databases to express a certain relationship.
Djangos models are just abstraction of the database design below.
E.g you specified a many-to-many relationship between Room and FurniturePiece.
Now the question: Is this what you want? It means that a piece of furniture can belong to more than one room. This sounds strange. So maybe you want to model it that a piece of furniture only belongs to one room. But a room should still have several pieces of furniture. We therefore define a relationship from FurniturePiece to Room.
In Django, we can express this with:
class FurniturePiece(models.Model):
room = models.ForeignKey(Room)
type = models.ForeignKey(FurnitureType)
color = models.ForeignKey(FurnitureColor)
sqft = models.IntegerField()
name = models.CharField(max_length=200)
Maybe you should first learn about relational databases to get the basics before you model your application with Django.
It might be that this not necessary in order to create an application in Django. But it will definitely help you to understand whats going on, for every ORM not just Django's.

Why does each FurniturePiece need to have a unique name? It seems to me that if you remove that constraint everything just works.
(as an aside you seem to have accidentally dropped the models.Model base class for all but the Room model).

This is how I would do it:
class Room(models.Model):
name = models.CharField(max_length=255)
pieces = models.ManyToManyField('FurniturePiece')
class FurniturePiece(models.Model):
itemid = models.CharField(max_length=20, unique=True) # This is what I would require to be unique.
name = models.CharField(max_length=255)
type = models.ForeignKey('FurnitureType') # Note I put 'FurnitureType' in quotes because it hasn't been written yet (coming next).
color = models.ForeignKey('FurnitureColor') # Same here.
width_in_inches = models.PositiveIntegerField()
length_in_inches = models.PositiveIntegerField()
# Next is the property decorator which allows a method to be called without using ()
#property
def sqft(self):
return (self.length_in_inches * self.width_in_inches) / 144 # Obviously this is rough.
class FurnitureType(models.Model):
name = models.CharField(max_length=255)
class FurnitureColor(models.Model):
name = models.CharField(max_length=255)
Envision objects as real life objects, and you'll have a deeper understanding of the code as well. The reason for my sqft method is that data is best when normalized as much as possible. If you have a width and length, then when somebody asks, you have length, width, sqft, and if you add height, volume as well.

Related

Managing inheritance in Django

So, in our database we had a Car model, but there could be different kinds of cars, with different fields and different processes associated to them, so I made some model classes that inherit from that Car, it's something like this:
class Car(models.Model):
field = models.CharField(max_length=300)
class CarA(Car):
field_a = models.CharField(max_length=300)
class CarA1(CarA):
field_a1 = models.CharField(max_length=300)
class CarA2(CarA):
field_a2 = models.CharField(max_length=300)
class CarB(Car):
field_b = models.CharField(max_length=300)
Also, there are trips, and each trip has a car associated to them:
class Trip(BaseModel):
car = models.ForeignKey(Car)
If I have a CarA1 instance, car_a1, I can save it in a Trip with car_a1.car_ptr, but it would be saved as a Car, and from there I don't think there's a way to get the CarA1 instance again except searching by the Car id in all the subclasses.
So, if I want to know which kind of car the trip has, or get a specific subclass field, it would be really complicated.
What do you suggest to do? I can just keep only the Car class with the specific fields nullable (field_a, field_b, etc), and a field to specify the kind of car, but that doesn't seem right, I would have to check on the kind to validate the fields, etc. But right now it seems like the better solution.
I'm using Django 1.11 and Python 3.6 btw.
Sorry I didn't use real code, I thought it would be way more complicated, also sorry if I didn't explain it very well, I'm sleep deprived and stressed.
Thanks!
The problem in your model is when a new type of car introduced say car c you need to create another table for that. It is not ideal and what you need is one table for store the car details(commonly used) and separate table say Properties to store other details and give a foreign-key relation to Car model.
First, you can get to the child model from the parent model (see model based inheritance).
If you want to stick with your inheritance based design, you could define a field in Car called car_type which would correspond to the type of the child table. For example:
class Car(models.Model):
CAR_A = 'car_a'
CAR_B = 'car_b'
CAR_TYPE_CHOICES = (
(CAR_A, 'Car_A'),
(CAR_B, 'Car_b'),
)
field = models.CharField(max_length=300)
car_type = models.CharField(max_length=20, choices=CAR_TYPE_CHOICES)
And then you could override the save method of the child models so that their car_type field is the one that corresponds to their model.
class CarA(Car):
field_a = models.CharField(max_length=300)
def save(self, *args, **kwargs):
self.car_type = Car.CAR_A
super(CarA, self).save(*args, **kwargs)
# ... similarly for CarB
Now you can get to the child model from Car based on its car_type.
if car.car_type === Car.CAR_A:
print(car.cara.field_a)
Note that this will start to get cumbersome if you have many levels of inheritance, so I would encourage you to reconsider your design.

Django/Python: Best Practice/Advice on handling external IDs for Multiple Multi-directional External APIs

So this is more of a conceptual question, and I am really looking for someone to just help point me in the right direction. I am building a middleware platform where I will be pull data in from inbound channels, manipulating it, and then pushing it out the other door to outbound channels. I will need to store the external id for each of these records, but the kicker is, records will be pulled from multiple sources, and then pushed to multiple sources. A single record in my system will need to be tied to any number of external ids.
a quick model to work with:
class record(models.Model):
#id
Name = models.CharField(max_length=255, help_text="")
Description = models.CharField(max_length=255, help_text="")
category_id = model.ForeignKey(category)
class category(models.Model):
#id
name = models.CharField(max_length=255, help_text="")
description = models.CharField(max_length=255, help_text="")
class channel(models.Model):
#id
name = models.CharField(max_length=255, help_text="")
inbound = models.BooleanField()
outbound = models.BooleanField()
Obviously, I cannot add a new field to every model every time I add a new integration, that would be soooo 90s. The obvious would be to create another model to simply store the channel and record id with the unique id, and maybe this is the answer.
class external_ref(models.Model):
model_name = models.CharfieldField()
internal_id = models.IntegerField()
external_id = models.IntegerField()
channel_id = models.IntegerField()
class Meta:
unique_together = ('model', 'internal_id',)
While my example holds simply 4 models, I will be integrating records from 10-20 different models, so something I could implement an a global level would be optimal. Other things I have considered:
Overwriting the base model class to create a new "parent" class that also holds an alpha-numberic representation of every record in the db as unique.
Creating an abstract model to do the same.
Possibly storing a json reference with channel : external_id that I could ping on every record to see if it has an external reference.
I'm really an open book on this, and the internet has become increasingly overwhelming to sift through. Any best practices or advice would be much appreciated. Thanks in advance.
I have this exact issue and yes there is not much information on the web in using Django this way. Heres what Im doing - haven't used it long enough to determine if its "the best" way.
I have a class IngestedModel which tracks the source of the incoming objects as well as their external ids. This is also where you would put a unique_together constraint (on external_id and source)
class RawObject(TimeStampedModel):
"""
A Table to keep track of all objects ingested into the database and where they came from
"""
data = models.JSONField()
source = models.ForeignKey(Source,on_delete=models.PROTECT)
class IngestedModel(models.Model):
external_id = models.CharField(max_length=50)
source = models.ForeignKey(Source,on_delete=models.CASCADE)# 1 or 0
raw_objects = models.ManyToManyField(RawObject,blank=True)
class Meta:
abstract = True
then every model that is created from ingested data inherits from this IngestedModel. That way you know its source and you can use each external object for more than 1 internal object and vise versa.
class Customer(IngesteModel):
class Order(IngestedModel):
...
etc.
Now this means there is no "IngestedModel" table but that every model has a field for source, external_id and a reference to a raw object (many to many). This feels more compositional rather than inherited - no child tables which seems better to me. I would also love to hear feedback on the "right" way to do this.

Giving a model knowledge of a many-to-many related model in django

EDIT: Given responses in comments and answer I tried suggestion and I get some errors when trying to query , also doing the related name query does not get the right results (as seen in comments)
BusinessLocations.objects.all()
Error: QuerySet object has no attribute 'objects' is the error.
In either case, I did a dump of all the tables and see this:
auth_business_permissions', u'auth_permission', u'auth_user', u'auth_user_businesss', u'auth_user_user_permissions', u'django_admin_log',
u'django_content_type', u'django_migrations', u'django_session', u'ipaswdb_address', u'ipaswdb_billing', u'ipaswdb_billing_businesss',
u'ipaswdb_designation', u'ipaswdb_business', u'ipaswdb_business_business_locations', u'ipaswdb_businessinsurances', u'ipaswdb_businessinvoices',
'ipaswdb_businesslocations', u'ipaswdb_businessterm', u'ipaswdb_insurance', u'ipaswdb_insurance_businesss', u'ipaswdb_invoice', u'ipaswdb_employee',
u'ipaswdb_employeeinvoice', u'ipaswdb_employeelocations', u'ipaswdb_employeeterms', u'ipaswdb_specialty']
I have a ipaswdb_business_business_locations and a ipaswdb_businesslocations which seems strange to me, and I wonder if my database is just gunked up?
Original Question:
I have two models a Business and an Employee. I want them both to be aware of each other but not directly but through another model called a 'BusinessesLocation`. I can sort of express this in my models but it doesn't look or feel right. It is like only the employee knows of the businesses, and not vice vice versa.
I had another question opened to try to answer this but the answer was not 100% correct in that it didn't offer for a many to many it was more like a one to many. In this case: An employee can work at many locations (potentially being an employee of many businesses) and a business can have many locations having many employees.
Currently my models work where this shell script works:
someEmployee.business_locations.all()[0].business.business_name
and it works fine, I can get all the locations of a business an employee works at and via that infer the many businesses an employee might work for given the businesses locations.
But I cannot figure out how to go the other way, and find out all the employees a business has working for them and at which locations
My current (wrongish) models are like this:
class Employee(models.Model):
first_name = models.CharField(max_length = 50)
business_locations = models.ManyToManyField('BusinessLocations', through='EmployeeLocations')
class EmployeeLocations(models.Model):
employee = models.ForeignKey('Employee', on_delete=models.CASCADE)
business_location = models.ForeignKey('BusinessLocations', on_delete=models.CASCADE)
created_at=models.DateField(auto_now_add=True)
updated_at=models.DateField(auto_now=True)
def __str__(self):
return self.provider.first_name
class BusinessLocations(models.Model):
address = models.ForeignKey('Address', on_delete= models.SET_NULL, null=True)
business = models.ForeignKey('Business', on_delete=models.CASCADE)
doing_business_as = models.CharField(max_length = 255)
created_at=models.DateField(auto_now_add=True)
updated_at=models.DateField(auto_now=True)
def __str__(self):
return self.doing_business_as
class Business(models.Model):
business_name = models.CharField(max_length=50)
business_locations = I need something here no idea how
Bellow is some pseudo shell code demonstrating how I would like my models to work:
#create a new business location assume business has been created
newLocation = Address(...)
business.business_locations.add(newLocation, doing_business_as='alternative name maybe')
#assume employee exists
#add a new business location to the employee
#when i say selected business the form would have current employee then in its locations
#you'd have to select a business first, and get a list of all that businesses locations and you
#you could add the business location and then select another business with all ITS locations
# and add one there too if you wish
employee.employee_locations.add(selectedBusiness.business_locations[0])
employee.employee_locations.add(anotherSelectedBusiness.business_locations[1])
Below is what I cannot figure out how to do, vice versa...
#now lets see which businesses the employee works for.
for business in employee.business_locations
business.business_name
#and lets see each businesses employees:
for employee in Employee.objects.all()
employee.
?? No idea how to build the models to represent these relationships
I can get an employees business locations just fine, but I cannot get the above examples of getting a list of employees for a business. Not sure what I need to adjust (or methods I might need?) to get this to work like I want in my shell example.
What you're missing is Django's concept of related objects.
When you define a relationship in a model (i.e., a ForeignKey, OneToOneField, or ManyToManyField), instances of that model will have a convenient API to access the related objects.
You can access the related objects both in queries and as a manager attribute on your models. See the examples in the documentation. In your case this would look something like:
# Now lets see which businesses the employee works for:
Business.objects.filter(businesslocations__employee=employee).distinct()
# And let's see each business's employees:
Employee.objects.filter(business_locations__business=business).distinct()

Django models with same or similar fields

I started to learn django and already have one question:
what kind of model creation are better if tables have similar fields?
1) Standart way form official tutorial like
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
class Restaurant(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
serves_hot_dogs = models.BooleanField()
serves_pizza = models.BooleanField()
As you see both models have same name and address fields. I found in https://docs.djangoproject.com/en/1.6/topics/db/models/#multi-table-inheritance the Multi-table ingeritance which works like this:
2)
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
class Restaurant(Place):
serves_hot_dogs = models.BooleanField()
serves_pizza = models.BooleanField()
As doc says: All of the fields of Place will also be available in Restaurant, although the data will reside in a different database table.
Well, is there any principal differences beteeen this two? And which one shall I use?
In this case, I think you should use inheritance (solution 2).
Because the Restaurant is a special Place, which means Restaurant have field name and address, additionally, Restaurant have field serves_hot_dogs and serves_pizza.
Considering the above description, all solution is reasonable. But, Restaurant is a Place, Restaurant must can be processed as Place.
In solution1, Restaurant and Place are separated, you cannot process Restaurant as Place.(Just considering OOP)
In other side, in solution1, if I save a Place with address A, and I add a Restaurant with same address. I save same address twice in database, it leads to redundant and inconsistency.
This question is similar with the different of Composition and Inheritance in OOP.
Yes, there is a difference and you've just pasted link to documentation, which explains this difference.
Restaurant db table in (2) consists of two extra fields (serves_hot_dogs and serves_pizza) and a ForeignKey to record in Place db table.
As per the question which model you should use -- whatever suits you best. I usually use multi table inheritance in similar cases.
Before jumping into table inheritance please see: https://django-model-utils.readthedocs.org/en/latest/managers.html#inheritancemanager. I use it to limit the number of queries when dealing with multi table inheritance.

Proper/most efficient way to handle Django models and relational data

I'm curious about what the best way to handle models in Django is. Let's say you want to make an app that deals with TV Show listings. One way to handle the model would be
class TVShow(models.Model)
channel = models.CharField()
show_name = models.CharField()
season = models.CharField()
episode = models.CharField()
Which has the advantage of everything being packed neatly. However, if I want to display a list of all of the channels, or all of the show_names, I would have to go through the TVSHow objects and remove duplicates
On the other hand one could
class CommonModel(models.Model)
name = models.CharField()
class Meta:
Abstract = True
class Channel(CommonModel)
show_name = models.ManyToMany(ShowName)
class ShowName(CommonModel)
seasons = models.ManyToMany(Seasons)
class Season(CommonModel)
episodes = models.ManyToMany(Episodes)
class Episode(CommonModel)
This would make it easy to show all of the ShowNames or all of the Channels, without having to worry about unrelated data. However, it would be much harder to see what Channel a show is, unless you map back as well
Is there a "pythonic" or Django preferred way to do this? Are there any advantages in terms of space, speed, etc?
Thanks!
Your initial stab at it looks fine. That is, you could use
class TVShow(models.Model)
channel = models.CharField()
show_name = models.CharField()
season = models.CharField()
episode = models.CharField()
And then you could just use the django orm to do the queries you were looking for.
That is, if you wanted all the channels with no duplicates, you would say
TVShow.objects.distinct('channel')
Django documentation for distinct().
As far as performance goes, this is the way to do it because you are effectively having the database do it. Databases are designed for these purposes and should be significantly faster than trying to trim it in code.
Preferred way to use normalized database structure unless it's performance-related, it will give you ability to make more complex queries in your code easier. ForeignKey and ManyToManyField accepts 'related_name' argument.
class Channel(models.Model):
pass
class Show(models.Model):
# this means you can have same show on different channels
channels = models.ManyToManyField(Channel, related_name='shows')
class Episode(models.Model):
# this means that one episode can be related only to one show
show = models.ForeignKey(Show, related_name='episodes')
Channel.objects.filter(shows__name='Arrested Development')
Channel.objects.get(name='Discovery').shows.all()
Show.objects.get(name='Arrested Development').episodes.all()
#2 db queries, 1 join
Episode.objects.get(name='Arrested Development S01E01',
select_related='show').show.channels.all()
#1 db query, 3 joins
Channel.objects.filter(shows__episode__name='Arrested Development S01E01')
and so on...

Categories

Resources