Django repeatable field with only new items - python

I want to create an Instruction that has multiple Steps. A simplified model would look like this:
class Step(models.Model):
description = models.CharField(max_length=255)
class Instruction(models.Model):
steps = models.ForeignKey(Step)
The problem is that I want to create a new Instruction with multiple steps, but when I create one in the admin, I should have a repeatable form field. For each step I can add a field and create a new Step. I do not need to be able to select an already existing step. I'm not sure if there is something OOTB of a package that already does that... Any ideas how to tackle this?
To give an example of what I'm trying to accomplish: the ACF repeater field in WP:
In my case I would only need a description field with the description of the step

You have things a bit backwards. The ForeignKey relationship should be the other way around (since an instruction can have many steps, but each step only has one related instruction...a Many-to-One relationship).
class Step(models.Model):
description = models.CharField(max_length=255)
instruction = models.ForeignKey(Instruction, related_name='steps')
class Instruction(models.Model):
# some fields
Now, in your Admin, you can use inlines to display these fields in a "repeatable" manner, similar to ACF.

Related

When to use each model relationship in Django?

I've been reading through the Django documentation and looking over some of the other answers on the site for a couple of hours now, yet I still can't get it to sink in. I know this isn't Django specific, but the examples I use will be from a Django project.
My question boils down to when is it appropriate to use each:
Many-to-many relationships
Many-to-one relationships
One-to-one relationships
One-to-one, more or less makes sense to me.
Now for the other two. While I understand the differences between them in isolation, when it comes to using them practically in a project, I get confused. Here is an example:
class User(AbstractUser):
pass
class Listing(models.Model):
title = models.CharField(max_length=64)
description = models.TextField()
class Watchlist(models.Model):
user = models.ForeignKey(User, related_name='watchlist', on_delete=models.CASCADE)
item = models.ManyToManyField(Listing)
class Comment(models.Model):
user = models.ForeignKey(User, related_name='comments', on_delete=models.SET_NULL)
comment = models.TextField()
Would this be the correct use of Many-to-one(ForeignKey) and Many-to-many?
Should Watchlist.item be a ForeignKey? Or is M2M correct?
Wouldn't it simplify to make the 'Watchlist' part of the User class? (give them an empty list to populate with listing ID's)
Why is Watchlist.user not a One-to-one relationship, if each watchlist belongs to a single user, and a user can only have one list?
Apologies for my stupidity, I just can't get this to sink in!
Thank you.
edit: Context, the models are from a 'learning' project I was working on intended to be an auction site, similar to eBay. The watchlist is sort of a 'wish' list... for the user to watch an item, not for site to watch a user!
To explain it simply these django-models or objects represents tables in your database and the fields are like the columns in them. So with a one-to-one relation you can only have one row in one table relating to one row in another table. For example one user in the user table (represented by one row) can only relate to one row in a profile table. But your user can have many comments, so this would be a one-to-many/foreignkey relation (if you set unique=true on a fk, it will in practice function as 1:1). If the users can collaborate on writing comments, or for example as here on stackoverflow where users can edit other users comments, that would be a many-to-many relation.
Database design can be complicated/complex, especially using an ORM without basic knowledge of SQL and how it all works beneath. In general it requires a bit of planning even for a simple application.

Django Python annotate ManyToMany Relationship with additional information

my problem is the following. I have two models in the database, which I link together using a ManyToMany relationship. For the admin page I currently use "admin.TabularInline" to bind different objects to one via the graphic. I still want to specify an order in the connections, preferably numbers which represent an order for processing. A bit more figuratively described I have the model "Survey" and the model "SurveyQuestion". So I connect many SurveyQuestions with the Survey. But I can't specify an order, because I don't have an additional field for it. It is not known before how many questions will be in a survey. Nor is it known which questions will be inserted. Usually they are built during the compilation of the survey and may be used later for another survey. I am grateful for every tip!
This can be achieved by defining a custom relationship table between the Survey and SurveyQuestion using through argument. For example you can define a relationship model:
class Question(models.Model):
question = models.CharField(max_length=256)
class Survey(models.Model):
name = models.CharField(max_length=256)
questions = models.ManyToManyField(Questions, through='Questionnaire')
class Questionnaire(models.Model):
survey = models.ForeignKey(Survey, on_delete=models.CASCADE)
question = models.ForeignKey(Question, on_delete=models.CASCADE)
question_order = models.IntegerField()
The details and example can be found here:
https://docs.djangoproject.com/en/3.1/topics/db/models/#extra-fields-on-many-to-many-relationships. If you do not want to mess up with the models, then you have to find out some hack like was proposed by Ronak Muhta.

Is there a way to have multiple instances of a model embedded into a single model field in Django?

I know the foreign key concept,Djongo Array field , however is there an alternative?
The issue with the foreign key concept is that I would need to make multiple hits to the database and the issue with Array field is the lack of information and the errors emerging without a known solution.
What I would like to do basically is in fact add multiple instances of a model say comments to a single field in another model but I would like to embed it rather than creating a new table for comments which I tried using an abstract model however it did not work out.
I would like to know any alternative solution.
You can use foreign keys, and to avoid making separate query for every related record you can extract them all at once using prefetch_related - https://docs.djangoproject.com/en/3.1/ref/models/querysets/#prefetch-related :
Returns a QuerySet that will automatically retrieve, in a single batch, related objects for each of the specified lookups.
Code example:
# models.py
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
class Comment(models.Model):
post = models.ForeignKey(Post, models.CASCADE)
text = models.TextField()
# Somewhere else, fetch posts together with related comments:
# It will take 2 requests in total
# - one to fetch posts, another to fetch all related comments
for post in Post.objects.all().prefetch_related('comment_set'):
print([c.text for c in post.comment_set.all()])

Django - Add several Images/Data to one field of a UserProfile model

I'm creating a UserProfile model where users can add as many or as few images to their profile as they wish. I've considered using an Image model like so:
class Image(models.Model):
related_profile = models.ForeignKey(UserProfile) # UserProfile is the name of the model class
user_picture = models.ImageField(upload_to=get_user_img, blank=True, null=True, height_field='height_field', width_field='width_field')
When somebody visits their UserProfile then all of their Image objects will be displayed; however when I want to edit the UserProfile (i.e. delete an image or two) but am unable to do this.
The 'instance' doesn't want to return more than one Image object to edit as I get error:
get() returned more than one Image -- it returned 2!
There's a similar question like this which suggested filter() as opposed to get() here django - get() returned more than one topic
though this uses a ManyToMany relationship, and the solution didn't work for my error.
Does anybody know any good ways to restructure this so that I can edit each model object from the same page (so not returning the aforementioned error)?
Like the title suggests, I'm wondering if it's possible to store a set of images as a list within one field of the UserProfile model because that's a potential idea.
You are on the right track. The Model.objects.get() method expects the query result to be one row (instance), which is then returned. However in your case the UserProfile can have any number of related Image's. So you need to iterate through the (possibly) multiple results you are going to get back from your query, and do something with each one. More like:
# there is only ONE UserProfile per userid.. that is to say, userid is a
# unique key.. so I can use get() to fetch it
profile = UserProfile.objects.get(userid=the_userid)
# now I need to get each image
for image in Image.objects.filter(user_profile=profile):
# do something with image....
If you only need the Image instances and don't need the UserProfile instance, then you can shorten this with a join:
for image in Image.objects.filter(user_profile__userid=the_userid):
# do something with image....
I should add that this has nothing to do with images, but applies any time you fetch data from the database using Django. Any query that has multiple rows needs to be done in this way.

Many to Many and Foreign Key relations in django admin

First my django knowledge is beginner level, so please be patient with me.
I am faced with a model relationship that I do not know how to handle. I have 3 models: Project, Location and SubLocation.
A project can have multiple locations and each location can have many sublocations. I have a many to many field for location in the Project model and a foreignkey field for location in the Sublocation model.
class Project(models.Model):
...
locations = models.ManyToManyField(Location)
class Location(models.Model):
name = models.CharField(max_length=250, unique=True)
class SubLocation(models.Model):
location = models.ForeignKey(Location)
name = models.CharField(max_length=100, unique=True)
In django admin, I am able to add multiple locations when creating a project(Using filter_horizontal). However, I also need the option to select sublocations based on an added location for the project being created. I did not know how to do it with the above approach.
I then removed the locations many to many field from the project model tried the approach below.
I created a ProjectLocation model and added it as an inline to the Project ModelAdmin to be able to add locations and sublocations when creating a project. The model that looks as follows:
class ProjectLocation(models.Model):
project = models.ForeignKey(Project)
location = models.ManyToManyField(Location)
sublocations = models.ManyToManyField(SubLocation)
However, the approach does not work as desired since you can add any sublocations irregardless of the locations added. What I would like is to be able to add locations and their relevant sublocations when creating a project.
I read through generic relations as another possible approach but still I did not know how to pull it off.
With my model structure, is that possible?
If True, what should I do to get the desired result?
If False, please recommend how I could change it.
I think if you use foreign key it will be easier for your case, and it will easy to use with the _set option from django.
Yes it is possible but it will not be dynamic (meaning that changing the value of location will not magically update themselves without first saving the change) which might be very unpractical.
You should use formfield_for_manytomany in your admin -> https://docs.djangoproject.com/en/1.8/ref/contrib/admin/#django.contrib.admin.ModelAdmin.formfield_for_manytomany
The easiest way to implement this would be to add custom javascript filtering on that particular admin form
Leave for models as it was and try to use inlines in admin page.
So, your admins.py would look something like this:
class SubLocationInline(admin.StackedInline):
model = SubLocation
#admin.register(Location)
class LocationAdmin(admin.ModelAdmin):
.....
inlines = [SubLocationInline]

Categories

Resources