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

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.

Related

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

Set Textarea size for all Textfields in ModelForm

I have a model with tons of (10-20) fields, some of which are Textarea fields.
All of the textare fields should have a certain size (which differs from the default). I could set them all using the widget dictionary in the Meta class, but then i'd have to set them one by one, which seems unnecessary redundant.
Is there any better way to solve this?
Update:
Since it was suggested in the comments to use database normalization, I'm going to flesh out my case a little bit:
I have an model object which has the main purpose of taking in a lot of user data (i.e. an "application"). The benefit of just writing out every question to the user as one field is that I can easily create a model form for it and display and edit the data in the admin (which is good for my crm people as well).
I could create a dictonary like model for every field of the form but I'm not sure how I would go about actually using a ModelForm for creating it.
On the basis of your data, I can only offer this approach:
from django import forms
textarea_fields = ['field1', 'field2']
form = YourForm()
for field_name in [f for f in form.fields.keys() if f in textarea_fields]:
form.fields[field_name].widget = forms.Textarea()

Django repeatable field with only new items

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.

Passing a model instance, not __unicode__ method in django

I've got a django form that contains a join via a foreign key. This is a foreign key to a very large table. On the form, to prevent loading up a massive select that tends to crash browsers, I've got a jQuery autocomplete, which on each keystroke sends off the entered text. This text is then searched in the table and suitable results are returned to be displayed. The id is then passed to a hidden CharField when one is selected. This hidden CharField is the widget for the ForeignKey relation. When I try to save the form, I get an error that I need to be passing a model instance for the related model, which is fair enough. I can't work out how to do this however. I can take that id and do a model.objects.get(pk=id_from_form), but if I replace the POST data with the result of this, I still get an error as I'm just passing the __unicode__ method of the model. I'm sure there's something I'm missing, but I can't see what it is.
Thanks.
Instead of using a CharField to store the id, try using a ModelChoiceField with the widget set as a HiddenInput. The field definition in your form would look something like:
mymodel = forms.ModelChoiceField(widget=forms.HiddenInput, queryset=MyModel.objects.all())

displaylist of children in parent model one-to-many

I am learning django admin, i have to models with a one-to-many relation between them.
I got something like Manufacturer model where i can add different car manufacturers, and Car model for adding cars. In my django admin page i want to be able to display_list of all cars manfuctred by say manufacturer1 when i click on manufacturer1 entry.
I have found a trick by using Inline model in manufacturer admin model, the problem is that it loads every entry in the database and it takes some time as it's a big table.
Is there any method else to do that or do i have to create a new template ?
EDIT:
The goal is not to load every Car that is FK to Manufacturer1 like with InlineModelAdmin but to get the same display as with display_list with results split in pages
Answer for your updated question:
the way to do it could be by using ProxyModels and overriding ModelAdmin.queryset
You extend the Car model via FordCar, GMCar and use proxy=True in the Meta class, for both those.
Then you can register seperate admins for each of FordCar and GMCar and override the queryset method in each of those ModelAdmin to filter for the respective manufacturer.
Ex:
class FordCarAdmin(admin.ModelAdmin)
list_display = fields = ['name','model','engine']
def queryset(self,request):
qs = super(MyModelAdmin, self).queryset(request)
return qs.filter(manufacturer__name='Ford')
admin.site.register(FordCar,FordCarAdmin)
You have two options.
The easiest approach is to look at the relationship in reverse. Instead of going to a manufacturer change form and seeing all their cars. Go to the cars changelist and filter by manufacturer. You'll have to set the list_filter attribute on the car ModelAdmin to include manufacturer.
Option two, is going to be a huge pain, but you can override change_view on the manufacturer ModelAdmin to add the list of that manufacturer's cars to extra_context. Then, you'll have to override the admin template at 'templates/admin/yourapp/manufacturer/change_form.html'. You can then add to that template to produce the kind of list you're looking for using the list of cars you passed into extra_context, drawing on 'django/contrib/admin/templates/change_list.html' for inspiration.
Give the Django docs on the Admin a good thorough read. There's actually a wealth of info in there.
You don't need any hack. Django admin displays only Cars that have a FK to Manufacturer1, when you select Manufacturer1, as long as you have used the InlineModelAdmin right and as intended.

Categories

Resources