I want to create a table that only adds some information about another table. Here is what I have:
I have 2 tables Exchange and ExchangeConnection:
class Exchange(models.Model):
slug = models.CharField(max_length=200, primary_key=True, blank=False, null=False)
name = models.CharField(max_length=200, blank=False, null=False)
class ExchangeConnection(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
exchange = models.OneToOneField(Exchange, on_delete=models.CASCADE)
There are multiple exchanges which are defined by me and won't be modified throughout the app usage. It's only useful stored data to make my app works. I see it more like a type of ExchangeConnection.
Then, I have ExchangeConnection. This table will be modified throughout the app usages because it represents information that belongs to a user, and it will be able to create some:
When the user create an ExchangeConnection, he select which only one Exchange he is using.
In my code you can see I'm using OneToOneField which isn't good because It implies an Exchange can be "linked" only once for every ExchangeConnection.
I don't want to use ForeignKeyField in ExchangeConnection because I don't want ExchangeConnections to be part of an Exchange.
I certainly misunderstood the logic of relations so I may bot be clear...
Short Answer:
ForeignKeyField is the correct option in this case.
Why? (Long Answer)
This statement says that you are thinking in a wrong way.
I don't want to use ForeignKeyField in ExchangeConnection because I don't want ExchangeConnections to be part of an Exchange.
Let's put it this way:
When you use ForeignKeyField in ExchangeConnection, Exchanges will be a linked to or part of ExchangeConnection.
ExchangeConnection won't be a part of Exchange.
For example, lets assume that you have used OneToOneField. There are two users, Sam and Richard.
You have saved first ExchangeConnection object with user = Sam and exchange = Exchange1.
Then you can't save the second ExchangeConnection object with user = Richard and exchange = Exchange1.
What happens in this case?
ExchangeConnection object should not have more than one instance which contains exchange = Exchange1. In other words it should not contains duplicates.
Conclusion:
OneToOneField = ForeignKey + (unique=True)
Related
I am trying to link venues to the products they supply. The products supplied are not unique to each venue.
As a result, Venue 1 and 2 could both provide Product A.
The outcome I am looking for is twofold:
when a Product is added to the database, there is an option to link it to an existing Venue
When looking at a venue in particular, I would like to have the list of all the product that can be supplied
Outcome 1. and current problem
I tried using Foreign Keys and ManyToManyFields but this only seems to add all the products available to the database to all the venues without leaving a choice.
This is what venue = models.ManyToManyField(Venue, blank=True, related_name="available_products") renders in the admin panel. In this example, by adding ManyToMany Field all Venues have been added to Product 1. Whereas I would like the possibility to add only specific venues (not all)
Outcome 2. and current problem
The second problem is obviously referring to Product from the Venue model. If I input a foreign key or any form of relation in it, Django gets upset and tells me Product is not defined.
I thought of creating a 3rd model, that could combine both Venue and Products, but it feels like there must be something more sophisticated that could done.
(edit: I replaced the FK by ManyToManyField as suggested by David Schultz)
class Venue(models.Model):
name = models.CharField(verbose_name="Name",max_length=100, null=True, blank=True)
class Product(models.Model):
name = models.CharField('Product Name', max_length=120, null=True)
venue = models.ManyToManyField(Venue, blank=True, related_name="available_products")
A ManyToManyField should in fact be perfect for what you want to do. It only associates those objects to one another for which relations have been explicitly created, e.g. in the admin or programmatically. The fact that your admin shows you all objects at once does not mean that they have been assigned, but just that they are available. In the list from your screenshot, selection can be done by Ctrl+Mouseklick, and when you then save the Product and reload the page, precisely the Venues you selected before should now again show up with a distinct background color – this means that they have indeed been saved.
Regarding your second problem: The argument related_name works differently than you apparently think: In your last line of code, you should rather write something like related_name="available_products", because related_name becomes the name of an attribute of your Venue instances, by which you can then access all Product objects that have been associated to that Venue object, e.g. like so: venue.available_products.all()
related_name works the same for ManyToManyField and ForeignKey.
You can define your ManyToManyField either on Product or on Venue; some more info is in the documentation page. So all in all, you should do something like:
class Venue(models.Model):
name = models.CharField(verbose_name="Name",max_length=100, blank=True)
class Product(models.Model):
name = models.CharField('Product Name', max_length=120, blank=True)
venues = models.ManyToManyField(Venue, blank=True, related_name="available_products")
(Side note: For CharFields, it is recommended not to set null=True and instead only use blank=True, because otherwise there would be two different options for "no data", namely Null and an empy string. Details in the docs.)
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.
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()
I’m writing simple application with Django and PostgreSQL for managing a home book library, where I can have many borrower profiles for people who borrowed books (Borrower model). And I have users, who can borrow a book themselves, so that the book becomes borrowed by the user’s borrower profile.
On the other hand admin can lend books to any borrower, even to one unregistered as a user.
So I have a few MyUsers (and that field, btw, references Django’s User) and many Borrowers, and I want to create a one-to-one relation between them, but every MyUser has to reference one unique Borrower, but many Borrowers will not reference any existing MyUsers (they can only reference one or none, or in another words, be referenced by only one or no user).
My question is: how to model that optimally? Using models.OneToOneField, models.ForeignKey and which model should reference which?
I will probably have many borrowers, who do not have user accounts.
Natural solution seems to be OneToOneField(Borrower, null=False) in a User model. But then when searching for users based on borrowers I will have to mostly deal with DoesNotExists exceptions and only once in a while I will get a proper result.
I can also make ForeignKey(Borrower, unique=True, null=False) – then I will have to check sets having single element or empty.
And I can make ForeignKeys both ways:
class Borrower(models.Model):
# ...
user = models.ForeignKey(MyUser, unique=True, null=True)
class MyUser(models.Model):
# ...
borrower = models.ForeignKey(Borrower, unique=True, null=False)
That implicitly defines the relations and I can easily make searches both ways, but it also makes one additional, redundant field in database tables.
I will probably just stick with OneToOneField for now, but I’d like to know which approach makes most sense in this case. What are the proc and cons? And is there an alternative, better solution?
I would stick with the OneToOneField; as you say, it is the most natural solution.
The only downside you mentioned is that borrower.user can raise a DoesNotExist exception. If you don't like that, you can always define your own method (or property) to return something else (like None) instead. Something like:
class MyUser(models.Model):
borrower = models.OneToOneField(Borrower, null=False)
class Borrower(models.Model):
#property
def user(self):
try:
return self.myuser
except DoesNotExist:
return None
#user.setter
def user(self, user):
self.myuser = user
For example, I have two models in a car management system(webpage):
class Brand(models.Model):
brand_name= models.CharField(max_length=100, null=False)
class Cars(models.Model):
car_model= models.CharField(max_length=100, null=False)
car_production_year= models.CharField(max_length=100, null=False)
car_brand= models.ForeignKey(Brand, null=True, blank=True, default = None)
Now, I want to delete a Brand data from the car system. How can I check if this Brand has been used in another model or does that foreign key contains any data(as I have allowed null True for car_brand in Cars model).
PS:
using this function:
self.model._meta.get_all_related_objects():
I got any related object in used in Brand model class. But, I don't know how to get if that related object contains any data.
Use exists(). It's designed to be used for exactly this type of situations:
for brand in Brand.objects.all():
if not brand.cars_set.all().exists():
# delete
Also it's almost always faster than any other type of check because of the way it is designed to work at the database level. You can read details of exists() behaviour in the docs
From this question, for my code, this was faster:
for b in Brand.objects.filter(cars__isnull=True):
# delete
If You have the ids (my case), I used this (60% faster than cars_set):
for brand_id in brand_id_set:
if Brand.objects.filter(id=brand_id).filter(cars__isnull=True):
# delete
I am using Django 1.11
I'd say the easiest way is like so:
# brand is an instance of Brand
if not brand.cars_set.all():
# delete
else:
# do something else
The Django docs cover foreign keys in quite a lot of detail.
cars_with_brands = Car.objects.filter(car_brand__isnull = False)
if cars_with_brands.count() == 0:
# you can delete Brand Model
else:
cars_with_brands.delete()
# you can delete Brand Model