I have 2 models Listing and Image, where a listing can have multiple images.
class Listing(models.Model):
title = models.CharField(max_length=100, blank=False)
class ListingImage(models.Model):
listing = models.ForeignKey(Listing, on_delete=models.CASCADE, related_name='images')
image = models.ImageField(blank=True)
Ideally, I wanted to create such listings on a single page e.g. fill in all form information AND add images on the same page.
It seems that this is not possible (if it's please let me know, but note that dropzone.js is Ajax) to handle 2 forms with a single view and I have made the following workarounds:
Create listings first, add 2nd step to add listings to created
model
Create a placeholder listing, add images to it (via Ajax)
and on form submit, change the placeholder information.
Which method is better?
views.py
def add_image(request):
# process images: problem we have no listing_instance to add them to
if request.method == 'POST':
form = ListingImageForm(request.POST, request.FILES)
if form.is_valid():
ListingImage.objects.create(pk=None, listing=listing_instance)
return HttpResponse(
json.dumps({
"result": True,
}),
content_type="application/json"
)
I'm looking for methodology advice. For example, to create a listing with images, do I need to first create the listing and then add images (2 steps) or add images first and listing information? My problem is that if I do that, the listing is not yet created when I add the images using Ajax.
I'll propose my solution which I've implemented in my projects and works like a charm.
form is rendered, dropzone.js initialized
images are submitted and uploaded to the defined endpoint (url), here django view saves uploaded file (as a model), returns saved image pk (or other unique identifier).
client front-end receives saved image pk and stores in the hidden input (for ex. named images) with some kind of protocol (for ex. pk1::pk2::pk3)
once form is submitted, django form validation has passed, you are processing input value with image pks and get entries from db:
images = ListingImage.objects.filter(pk__in=request.POST.get('images').split('::'))
I strongly recommend to apply more validation to images value
This query will give you all user uploaded images for submitted form, and finally you will update ListingImage entries to link Listing entry
images.update(listing=newly_created_listing_entry)
In addition, to avoid db bloating, I run cron job every 1 day to clean images which are not linked to any form (in case user abandoned form submission)
hope, this helps
Related
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.
I'm following this example to try and implement a way of uploading images from the default page of my application in web2py into the database. I went through the whole thing and can now view the images on the default page that were uploaded through the appadmin controller and the function /insert/db/image
What I would like is to have a small section on the default page to upload images from rather than having to go to a different page in /appadmin/insert/db/image
How would I implement this in the default controller and view? Thank you
You could simply add a SQLFORM to the index page:
def index():
[your existing code]
form = SQLFORM(db.image).process()
return dict(..., form=form)
In /views/default/index.html, wherever you want the form, add:
{{=form}}
Alternatively, you can use a grid, which provides an entire interface for listing, creating, and editing database records. You would replace the form lines above with:
grid = SQLFORM.grid(db.image)
return dict(..., grid=grid)
and:
{{=grid}}
Note, by default, all grid URLs are signed, so to create/edit/delete records, the user must be logged in (to disable this protection, set user_signature=False).
This is all covered in the forms chapter.
I'm developing an API (using DjangoRestFramework) where I want the user to be able to load a list of their items, but I only want to load a few columns from the database (let's say ID, Title, Category, and let's say the URL they are accessing is api/items).
I also want them to be able to send a POST request to this URL containing not only the columns that the page loads, but extra ones as well (example: a form with Title, Category, Date, Rating). If they want to view the item in full, including these extra details, they can view the individual items page (api/items/246).
From my understanding at the moment, the way you select which fields are loaded, is through a serializer class in the model's model.py file. So I have created a listSerializer and a detailSerializer, with the fields I want to load for each listed in the meta class. These work fine for getting the information I want.
My problem is when a user tries to send their POST request adding a new item: the only fields that are saved are the ones listed in the listSerializer's meta class. How can I go about saving the entire object?
Thanks in advance, any help is greatly appreciated.
You can set write_only attribute of fields to True and this will solve exactly your problem. Write only fields will not show on the response but will accept your posted data.
class ListSerializer (serializers.HyperlinkedModelSerializer):
title = serializers.CharField(write_only=True)
Hi I have this type of model
class Post(models.Model):
title = models.CharField()
image = models.ImageField()
# other fields
For creating a new post I would a flow like this:
in the new post page 1 the user choose the photo and upload it
(submit)
in the new post page 2 the user can see the photo and fill other fields like 'title'
My question is how to upload a photo and display it in the next page, without creating a new Post object with the image field. Should I save the image in a temporany directory? In this case, how can I keep the reference and use it in the second page?
Thanks.
There are couple of solutions
Define another model to just hold images, and add may be OneToOneField in your Post model. On submitting page1, create instance of this image model and put it in page2 as image field.
With html5 file API, you can refer to local file in image field. So you can show image on page2 using local client side path w/o storing it at server. refer: View image selected from file-system on client-side before upload?
First read the relevant doc so you understant what happens with uploaded files:
https://docs.djangoproject.com/en/dev/topics/http/file-uploads/
The simplest solution IMHO would be to
1/ save the uploaded file in some temporary directory - just make sure you can serve the content one way or another (either directly thru the front webserver, or thru a custom view if you want more control on who can access this content),
2/ pass the temporary file path and url to the "next" view one way or another (hidden form fields, session...).
I use to have table to store images (any files in fact :)). Each image is linked to object (FK) and has status - Draft, Approved, Deleted. So I don't use temporary directory, just change status in table of images.
I have a model, OrderedList, which is intended to be a listing of content objects ordered by the user. The OrderedList has several attributes, including a site which it belongs to.
The content objects are attached to it via an OrderedListRow class, which is brought into OrderedList's admin via an inline formset in the admin.
class OrderedList(GenericList):
objects = models.Manager()
published = GenericListManager()
class OrderedListRow(models.Model):
list = models.ForeignKey(OrderedList)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveSmallIntegerField()
content_object = generic.GenericForeignKey("content_type", "object_id")
order = models.IntegerField('order', blank = True, null = True)
(OrderedList inherits the site field from the larger GenericList abstract).
Here's my problem; when the user saves the admin form, I want to verify that each content object mapped to by each OrderedListRow belongs to the same site that the OrderedList does (the list can only belong to 1 site; the content objects can belong to multiple).
I can override OrderedList's admin form's clean(), but it doesn't include the inline formset which contains the OrderedListRows, so it can't reach that data. I can override the OrderedListRows' inline formset's clean, but it can't reach the list. I need some way within the context of form validation to reach both the OrderedList's form data and the formset's form data so I can check all the sites of the OrderedListRow's content objects against the site of the OrderedList, and throw a validation error if there's a problem. So far I haven't found a function that the cleaned data for both OrderedRow and the OrderedListRows are contained in.
In the inline formset, self.instance should refer to the parent object, ie the OrderedList.
I am dealing with the same issue. And unfortunately I don't think the answer above covers things entirely.
If there are changes in both the inline formset and the admin form, accessing self.instance will not give accurate data, since you will base the validation on the database and then save the formset which overwrites that data you just used to validate things. Basically this makes your validation one save behind.
I suppose the real question here is which gets saved first. After digging int he source code, it seems like the admin site saved the form first. This means that, logically, doing validation on the formset and from there accessing the 'parent' instance should get consistent values.