How to relate multiple models to a single model in Django? - django

I want to store all the data related to a specific device, and I called the model that stores that information File.
But for example a device with Motherboard, have multiple fields only related to the motherboard(model, brand, serie's number), and so on with the cpu, gpu, mouse, etc. Is there a way to include multiple models inside a model and create a form that join all of them inside the File model?
Like this:
class File(models.Model):
institute = models.CharField(max_length=200)
motherboard = models.ForeignKey(Motherboard, on_delete = models.CASCADE)
cpu= models.ForeignKey(Cpu, on_delete = models.CASCADE)
The problem I having with this aproach is that the create view just showed me a dropdown list in the motherboard and cpu fields, waiting for me to select one of the already existing ones, the expected behavior is the view displaying a form for each of the models and be able of see all the information as a whole under the File model.

Related

Django formset_factory vs modelformset_factory vs inlineformset_factory

Forms can be complicated in Django. Formsets can make you want to quit Django. I'm at that point.
What are the different use cases and considerations of which one(s) to use?
I'm looking for some better guidance as to when to use each factory, as they seem to depend on when you know what type of form, fields, and whether or not you are creating, editing, or deleting (individual forms entirely or the parent model altogether). I have read many walkthroughs, but am struggling to see the larger picture, especially as I am attempting to move from function based views to Class Based Views.
Below are some pseudo code with assumptions/restrictions to help you help me understand the differences. It may help to provide psuedocode, such as what kind of Form (ModelForm or regular) goes with the Formset, or what should be popped from the form, given this seems to be a trend for creating forms with relations.
Assuming you have some models:
class Dish(models.Model):
name = models.CharField(max_length=50)
class Meal(models.Model):
name = models.CharField(max_length=50)
dishes = models.ManyToManyField(Dish,
# through='OPTIIONALMealDishIntermediaryClassTable',
related_name="meal")
class Reservation(models.Model):
date = models.DateTimeField()
greeting = models.CharField(max_length=255)
meal = models.OneToOneField(Meal, on_delete=models.CASCADE)
class MealPhotos(models.Model):
photo = models.OneToOneField(Photo, on_delete=models.CASCADE, related_name='mealPhoto')
meal = models.ForeignKey(Meal, on_delete=models.CASCADE)
# optional, so a photo can be attached to a dish if the picture is just of the dish
dish = models.ForeignKey(Dish, blank=True, null=True, on_delete=models.CASCADE)
And you want to create a new Meal, you want to send a Reservation at the same time:
which factory would you use?
does it depend on if the forms are all ModelForms? (meaning how would you handle assignming the Meal its Reservation)
assuming:
at this stage you know which Meal it is, but you still have to make a Reservation at the same time/same view.
you don't know which Dishes you are going to cook, since the reservation will tell you.
MealPhotos won't exist yet since the meal isn't prepared yet.
You want to create the meal and the reservation on the same form/screen
Then later, you want to add some dishes, based on what the Reservation says:
which factory would you use?
does it depend on if the forms are all ModelForms?
assuming:
at this stage you know which Meal it is, and you have a Reservation
you are going to assign dishes to the meal based on the Reservation, and you have enough information to do so, and can use a ModelForm easily, but not required
Later, the person eating the dish wants to take some photos, and you don't know how many they will take
which factory would you use?
does it depend on if the forms are all ModelForms?
assuming:
we will require them to take at least two
we have access to the Meal, Reservation, and Dishes
a photo could optionally be assigned to a Dish
The difference between the 3 formset factories is basically:
formset_factory lets you render a bunch of forms together, but these forms are NOT necessarily related to a particular database models (this is not what you need, since you have models for everything)
modelformset_factory lets you create/edit a bunch of Django model objects together, for example, if you were managing the "menu" you could use a modelformset_factory(Dish, fields=('name'))
inlineformset_factory lets you manage a bunch of Django model objects that are all related to a single instance of another model. For example, if you wanted to manage all of the MealPhotos that were taken at a particular Meal, this is what you would use
To answer your specific scenarios:
If you wanted a page that created a single Meal and a single Reservation at the same time, without assigning any Dishes yet, you don't need any formsets. You could just use a ModelForm for Meal and a ModelForm for Reservation.
Later on, if you want to attach multiple Dishes to the Meal, you would use an inlineformset_factory(Meal, Dish) to edit multiple Dishes belonging to a single Meal
Since we are using an inlineformset_factory, we have to create the Meal instance in the view that renders the form. Something like this:
DishFormSet = inlineformset_factory(Meal, Dish)
bday_dinner = Meal.objects.create(name='30th Birthday dinner')
formset = DishFormSet(instance=bday_dinner)
For someone uploading photos of the Meal, you would use:
PhotosFormSet = inlineformset_factory(Meal, MealPhotos)
bday_dinner = Meal.objects.get(name='30th Birthday dinner')
formset = PhotosFormSet(instance=bday_dinner)
This tells Django that all the photos submitted are all linked to that one Meal, but allows the possibility of assigning each photo to a different Dish (via a dropdown in the form).
Note: In the first scenario, I haven't tested whether you the use of a ManyToManyField as Meal.dishes is supported by the formset factory. If it isn't, then you could simply use a ModelFormset(Dish) and after those are created, link them to the Meal in the Django view that process the form submission.

Django Admin list values from many to many fields

For a podcast site I do have a Guest model which (stripped down) looks like this.
class Guest(models.Model):
...
episodes = models.ManyToManyField(Episode)
name = models.CharField(max_length=100, blank=False)
...
also there are a few other models connected through many to many fields with the guest model like Job and Topic (simple models with one or two CharField with some information and the ManyToManyField relation with Guest.
Now I want to display The Job(s) of a guest and his Topics in the Admin List of Guests. Also for the linked Episodes Model I want to count the amount of episodes a guest was in and also show the date (published_at field in Episode model) of the newest and the oldest Episode the guest was in.
I tried a few things already but I can't seem to get the data from the many to many fields into that list display. I'm on Django 2.0.7
You cant just show, you make create one function and JOIN the values together...
class AdminGuest(admin.ModelAdmin):
list_display = ('_episodes')
def _episodes(self, obj):
return "\n".join([a.nome for a in obj.episodes.filter()])
_episodes.short_description = "List of Episodes"

django - many-to-many and one-to-many relationships

I am working in django, am planning a database for rides for users.
each User can be on multiple Rides (over time) and each Ride can have multiple Users (passengers) in it.
Also, for each Ride there has to be only one Driver (also a User) so I think I have a many-to many relationship between the Rides and Users tables for what user is on what ride, and also a One-To-Many relationship between the Rides's Driver_id and the User_id. right?
My questions are-
I saw in the django docs that I should put a many-to-many field in One of the models. Does it matter which one? and also, does it create a new table like rides_users?
and also, what is the difference (in One-To-many relationship) between using a foreignKey field and a OneToManyField field?
EDIT:
Currently, there are my models:
def get_image_path(models.Model):
return os.path.join('photos',str(instance.id),filename)
class UserProfile(models.Model):
user=models.OneToOneField(User)
phone_number=models.CharField(max_length=12)
profile_picture=models.ImageField(upload_to=get_image_path, black=True, null=True)
class Ride(models.Model):
driver=models.ForeignKey(UserProfile, related_name="r_driver")
destination=models.ForeignKey(Destination, related_name="r_final_destination")
leaving_time=models.DateTimeField()
num_of_spots=models.IntergerField()
passengers=models.ManyToMany(UserProfile, related_name="r_passengers")
mid_destinations=models.ManyToMany(Destination, related_name="r_mid_destinations")
class Destination(models.Model):
name=models.CharField(max_length=30)
As you can see, each Ride has multiple mid_destination and multiple passengers. a Ride also has One driver and One final destination.
The Issue is - when a User adds a Ride, I want the driver, destination and mid_destinations and the rest of the fields to be set by the User (the driver is user adding the Ride), Except for the passengers field. I want the other Users to add themselves to the ride, so when the Ride is created the User (driver) doesn't have to set the passengers.
How do I go about it? and also, any other suggestions about the models?
There is no such thing as a OneToManyField.
It doesn't matter from a practical point of view which side the ManyToManyField lives on. Personally, I'd put it on Ride, both to avoid changing the User model and because conceptually I'd say that rides are the main objects here.
And yes, adding the field will automatically create the linking table.
what you want is probably something like this
class MyModel(models.Model):
driver = models.ForeignKey(to=User, related_name='r_driver')
# you need to use a related name if you want to link to the same model more than once
passengers = models.ManyToManyField(User, related_name="r_passengers")

django remove least recent many to many entry

In my Django app, I want to allow users to see which profiles they view and which profiles view them. In my Profile model I have created 2 fields that accomplish this.
viewed = models.ManyToManyField('self', null=True, blank=True, related_name='viewed_profiles', symmetrical=False)
visitors = models.ManyToManyField('self', null=True, blank=True, related_name='visitors_profiles', symmetrical=False)
I also have the code set up in my views.py file to add profiles to these fields as necessary. However, I would like to only track and display the most recent 25 or so viewed and visitor profiles. Is there a way to query these fields ordered by date added and delete everything past the first 25 results? Is this possible without creating another field to track the order of the profiles viewed?
Take a look at the documentation on Querysets for details of how to do this. You can use order_by to order your objects by date, and use Python's array slicing syntax to limit the number of results.
An example of showing the most recently added items in your view might look something like this:
viewed = Profile.objects.order_by("-date_added")[:25]
This doesn't delete everything after 25 - it just fetches the 25 most recent objects (assuming your Profile model has a field called date_added).
EDIT: Oops, I think I misread your question.
I think what you'd need to do is have an intermediate model - Django allows you to use a third model as an intermediate one between two different models in a many-to-many relationship. Then you could add the time viewed to that model and store it that way. There's a good example in the documentation.
I wouldn't really bother deleting the old ones unless database space was likely to be an issue, but if you need to for any reason, I guess you could set up a signal that was triggered by a new view being created and have that call a function that deletes all but the 25 most recent.
Django doesn't track the date added for a ManyToMany relationship, so it's not possible to do this reliably without adding a field. To achieve this you'll need to do is add a date field on your ManyToMany intermediary table, then order by that - for example
class ProfileViewed(models.Model):
viewed = models.ForeignKey('Profile')
viewer = models.ForeignKey('Profile')
date_added = models.DateField(auto_now_add=True)
class Profile(models.Model):
...
viewed = models.ManyToManyField('self', null=True, blank=True, related_name='viewed_profiles', symmetrical=False, through=ProfileViewed)
Then you can order your results like so:
profile = Profile.objects.get(...)
views = ProfileViewed.objects.filter(viewed=profile).order_by('date_added')

Django - can you explain this?

I have three models:
System_Contact
System
Contact_list
The Contact_List model has two fields: contact and sys and, not surprisingly, is just a manyToMany model to associate a list of contacts to each system. I have modelForm for adding a new contact to the system's list of contacts:
class Add_Contact_Form(ModelForm):
class Meta:
model = Contact_List
fields = ('contact',)
Simple, right? My confusion is this: Even thought the Contact_List model has many many duplicate contacts (because one contact can be associated with many systems) each contact is only displayed once within the form's Select widget.
Why?!
I mean, this is a great default behaviour for my purposes, but I want to make sure this is actually the correct default behaviour that I can rely on, not some random error I have done that just happens to work out for me now.
It's not that it's default behaviour, it's that the select widget in your contact_list form is displaying all of the entries that are from the contact table.
Every model is a table in the database, therefore you have 3 tables:
ContactTable - where every row in the table is a person
SystemTable - where every row in the table is a computer (for arguements sake)
ContactListTable - where every row is a mapping between a system and a list of users
If this is what you are trying to do, you should have the following:
class Contact(models.Model):
name = ...
class System(models.Model):
type = ...
class ContactList(models.MOdel):
system = models.ForeignKey(System)
contacts = models.ManyToManyField(Contact)
This means that every row in the ContactList table is a relationship between a particular machine from the system table and a list of contacts from the contact table