Django - Models - Linking models to another and vice versa - python

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.)

Related

Django models - Model relations misunderstandings

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)

Django AutoSlugField not considering soft deleted instances by Django Safe Delete

In our model we have a name and slug fields. This is how it looks like:
class MyModel(CommonInfo, SafeDeleteModel):
name = models.CharField(_('Name'), max_length=255, null=True, blank=True)
slug = AutoSlugField(_('Url'), populate_from='name', unique=True,)
For the slug field we generate an unique slug every time our model is saved.
We are also using Django Safe Delete library to soft delete our model:
Django Safe Delete
Django Autoslug
That means that for example if we create a new instance of our model with Name "My Model" it will auto generate a slug that will look like this: "/my-model".
Now let's say we "soft delete" this instance with the slug "/my-model". In our database there will be a property deleted which contains the date when the model was deleted. We don't show this one in our application, it is completely ignored (because it is soft deleted, that's fine).
The problem is that next time we create another one with the same name "My Model" it will auto generate the slug "/my-model" again, not considering that there is already one (which is soft deleted) with the same name and slug. We would need something like "/my-model-1" or whatever that is unique.
We are missing the connection between the autoslug and the safe-delete libraries, somehow the autoslug needs to know that there might be soft deleted ones and consider them when generating the unique slug.
Any help would be really appreciated and please consider that we are totally new in Django / Python.
if this doesn't work, our workaround will be generating the slug using 2 fields (name & id). It will generate a slug that will look like this: "/my-model/12345" and will be always unique since id is unique.
I think we found it.
We needed to create a new Manager that can see all the instances, even the soft deleted ones:
class MyModelAllManager(SafeDeleteManager):
_safedelete_visibility = DELETED_VISIBLE
Then in our model we pass it to the AutoSlugField function:
class MyModel(CommonInfo, SafeDeleteModel):
# We don't know why but it only works if we create a variable and assign the Manager to it. It doesn't work if we pass the Manager directly to the AutoSlugField function.
all_objects = MyModelAllManager()
name = models.CharField(_('Name'), max_length=255, null=True, blank=True)
slug = AutoSlugField(_('Url'), populate_from='name', unique=True, manager=all_objects)
That does the magic.

Form to show unique values from Model Django

Learning Django.. I am trying to setup the below and need your expert advise to make it better..
I created a model, and form on top of it to show the data.. and I have two questions after the code where I need your advise :)
Model:
class profilestable(models.Model):
choices = [
('Active','Active'),
('Inactive','Inactive')
]
Category = models.CharField(max_length=100)
SubCategory = models.CharField(max_length=100)
product_name = models.CharField(max_length=100)
Status = models.ChoiceField(max_length=20,choices=choices)
def __str__(self):
return self.Category
class Userlist(models.Model):
User = models.CharField(max_length=100)
Categorygroup = models.ForeignKey(profilestable,null=true,on_delete=models.SET_NULL)
def __str__(self):
return self.User
Form:
class userprofileform(forms.ModelForm):
model = Userlist
Fields = ('User','CategoryGroup')
def __init__(self,*args,**kwargs):
super(userprofileform,self).__init__(*args,**kwargs)
Sample data in the profilestable Model:
Queries:
I want to add the category, subcategory and product name as drop downs to the form however I am not sure how to access each element to show on the form. I was able to pull only category since I am returning that value.
The list currently has lots of duplicate values, is it possible to show only unique values in the drop down.
Also, is it possible to make it a multi-select and dependent cascading drop downs
Request you to please help advise/direct to implement this type of form.
Thank you so much.
The structure needs some changes, for example category should be a parent of sub-category, and then product lives below sub-category, so it would be better to create a separates models for Category, Sub-category and Product in this relation:
Category > Sub-category > Product Since sub-category and products are not unique relation to its parent, use the ForeignKey Field to connect them.
Also I see in your sample data and many products are shinked in 1 row, that is not good in any terms of scaling, performance or order.
Yet I cannot see the usecase of your Userlist model.
Once done the above please refer this answer to create your drop down menu, althought the distinct at the end is not needed as creating separate model for Category will avoid the need to duplicate Category names. Then on form submition, you Query for the correct Category and Sub-Category instance and assign it to the ForeignKey field of your form.

Django Models: Linking recipes and keeping track of quantities

Problem Description
Suppose I have a database with multiple models running with a Django front-end.
One of the tables in the Inventory. The inventory consists of entries with the following specifications:
class InventoryItem(models.Model):
item_name = models.TextField(max_length=10) #apple, orange, cilantro, etc...
item_quantity = models.DecimalField(...)
The next model will be to describe what is made with those ingredients
class Product(models.Model):
product_name = models.TextField(...)
product_description = models.TextField(...)
The ProductItem model also needs to keep track of the ingredients taken from inventory by specifying the InventoryItem and the quantity used from that inventory item used.
Previous Experience
In a previous experience, I have done something similar with EntityFramework in C# with MySQL. The way I achieved that was using another table/model called RecipeElement, where each one of those would be foreign-keyed to a ProductItem entry. The RecipeElement model looked like the following:
class RecipeElement(models.Model):
inventory_item = models.ForeignKey(InventoryItem, on_delete = models.CASCADE)
quantity_used = models.DecimalField(...)
product_item = models.ForeignKey(ProductItem, on_delete = models.CASCADE)
The Issue
My issue with that approach in Django is twofold:
How would I retrieve the RecipeElement entries associated with a ProductItem entry
How would the user input the RecipeElement entries and the ProductItem entries on one page. (The number of RecipeElements for each ProductItem is not limited, but each RecipeElement is associated with only one ProductItem
I am using SQLite for the moment but plan to transfer to MySQL in the future, if that changes anything.
If you want to retrieve all the RecipeElement for a Product do something like:
ProductItem.objects.get(pk=1).recipeelement_set.all()
In the second issue you can add a recipeElement from a product using .add() or create() like:
ProductItem.objects.get(pk=1).recipeelement_set.add(your_recipe_element)

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()

Categories

Resources