I have a formset (more specifically a generic inline formset) whose forms have an ImageField. After working around what seems to be a few Django bugs, everything is working fine, except that when I fill the blank form with a new image and click save, the page refreshes after the POST request and the new image is not there. The other fields of the form I just saved are there and are properly filled, but the image URL doesn't show up. The database record is saved correctly and if I simply refresh the page, the image shows up correctly. But I can't figure out how to return from the POST with all the newly saved information without having to refresh the page an extra time.
In case it's relevant, for this ImageField I am using a custom Storage class which handles saving the image in a remote server through an API.
A workaround that solves the problem but which in my opinion shouldn't be necessary:
class ProductImagesView(View):
...
def post(self, request, id):
product = get_object_or_404(Product.objects.by_id(id))
image_formset = ProductImageInlineFormset(
request.POST, request.FILES, instance=product)
if image_formset.is_valid():
image_formset.save()
image_formset = ProductImageInlineFormset(instance=product) # Workaround
return render(...)
You can find more details about my code in this other question:
Django BaseGenericInlineFormSet forms not inheriting FormSet instance as form instance related_object
Any idea why this is happening? Am I doing something wrong or is this a Django bug? Thanks.
UPDATE
One thing I forgot to say: the formset that shows up after saving not only has a blank Image field for the newly created Image object, it also doesn't have the extra blank form for new records that should be there. It also only appears after a refresh.
(I pass extra=1 to the generic_inlineformset_factory):
ProductImageInlineFormset = generic_inlineformset_factory(
Image, form=ProductImageForm, extra=1)
Related
What I am trying to do I have a view which has a shortcut form which will ask a user to pre-fill some fields before a user is redirected to wizard forms.
For example:
class PreFillView():
def post(self,request):
# get the data from the form and save into request.session
# Then http redirect to the wizard view
Then from this view, I redirect it to a WizardView. In the wizard view, I catch all the information passed in from the previous view in dispatch function:
class MyWizardView(NamedUrlSessionWizardView):
def dispatch(self,request, *args, **kwargs):
#parse data from request.session
#set step data using these data
# Note these data fields only partially covered the form in the wizardview, there is still a couple of fields needed to be filled in the wizard view.
This almost works fine but the only problem is that it validates the form and pop up field error for the fields which are not pre-populated. I tried, if I only redirect to the wizard view without setting the step data, it is fine. It won't validate the form, so no field errors will be displayed.
I am pretty new to Django and not sure if I am doing the right thing and if yes, how can I avoid form to be validated after I set the step data for the current step? Any help will be appreciated.
Implement the WizardView.get_form_initial(step) method in you wizard view class.
This method gets step number as parameters and it should return dict for initial data for the form for that step.
I have gotten Creatview() class based function to work. When the submit succeeds, it has already the data and shows the 'success' page.
I'd like to change this behavior this way: When the CreateView() succeeds, I'd like the data to get validated, but not saved. Instead of going to the success page, I'd like to use the DetailView() class to display the newly created instance, so the user can see how it is going to look like when the data is eventually saved..
Once the user is happy with the data displayed, the user can click "save" in which case the data is saved and the CreateView() is completed or the user can click "re-edit", and go back to the form to change the data and then be shown the newly created instance using DetailView() (and repeat until the user is satisfied). What is the best way to do this using class based views elegantly?
from django.views.generic.edit import CreateView
from restaurant.models import Restaurant
from restaurant.forms import RestaurantForm
import uuid
class RestaurantCreate(CreateView):
form_class = RestaurantForm
template_name = 'restaurant_form.html'
model = Restaurant
def form_valid(self, form):
form.instance.created_by = self.request.user
form.instance.life_id = str(uuid.uuid1())
return super(RestaurantCreate, self).form_valid(form)
Also, I do know about Form wizard, but I do not have multiple page forms. Even if I ignore that, Form wizard's does not give the opportunity to preview data before the final save.
Edit: Related discussion on google groups, but no solutions
Here's what I should do:
Overwrite the form_valid method of the RestaurantCreate class and let the save the form in a session. From there you can redirect to another view , your RestaurentDetail view, there you would overwrite the get_object method by reading out the form out of the session and displaying what you need.
There I would also place a form with all fields hidden, except the submit/save button. The form will be populated by whatever was in your session. So when the user presses save a POST is done to another view RestaurantFinalCreate view for example. There you can just implement the CreateView as normal.
If you're uncertain which method to overwrite and how, take a look at: http://ccbv.co.uk/ it has been really helpful to me.
Also don't use super in the form_valid method of the RestaurantCreate view since that would trigger a save in the parent class ModelFormMixin.
I've posted about this problem before, but I still haven't found a solution so I'm hoping I'll have better luck this time.
I have a form that takes inputted data by the user. In another page, I am creating the identical form that the user has populated (pre-filled with that information) for editing purposes. Users will come to this page to EDIT the information they have already put in. My problem is that it isn't overwriting the instance.
def edit(request):
a = request.session.get('a', None)
if a is None:
raise Http404('a was not found')
if request.method == 'POST':
form = Name_Form(request.POST, instance=a)
if form.is_valid():
j = form.save( commit=False )
j.save()
else:
form = Name_Form( instance = a )
For this form, I'm using "unique_together" for some of the values. I'm also calling on `{{ form.non_field_errors }} in the template.
What is happening is when I make changes in the editing view, if the fields changes involves those defined in "unique_together" then an error is returned telling me that the instance already exists. Otherwise it saves a new instance. It isn't OVERWRITING.
Note that the reason i am using unique_together is that I want to prevent users from initially inputting the same form twice (before the editing stage, in the initial inputting view).
Any ideas?
EDIT: note that "a" refers to a session that includes a drop down box of all the available instances. This carried forward will indicate which instance the user wants to edit.
`
Why not do a database lookup of the model your trying to save and pull the fields from the form to the model then save the model?
Instead to store model a in session you should store it on database. Then edit it:
def edit(request, pk):
a = A.objects.get( pk = pk)
...
pk it the a identifier, you can send it to view via urls.py. I encourage to you to use POST/Redirect/GET pattern.
You can add a 'state' field on your model to control workflow (draft, valid)
You should not save objects in the session. If you really need to use a session - save a PK there and retrieve object right before giving it to Form. But the better solution is to send it in GET or POST parameters or included in url. Sessions are unreliable, data inside it can be destroyed between user's requests.
And you can retrieve value from a session in a more pythonic way:
try:
a = request.session['a']
except KeyError:
raise Http404('a was not found')
Here's the use case:
My main page has an image upload form. When the form is submitted, a page loads with a 4x4 grid of the uploaded image with different photo filters applied. Clicking on an option saves the image with the chosen processing to the database.
My question is, how would I go about doing this without saving to the database until the processing has been chosen?
I'm not opposed to using this problem to learn ajax.
Do it in a single view; There are three possibilities it has to take into consideration:
The user has just arrived on the page - server them an empty form (InitialImageForm)
The user has submitted an image - don't save it, instead create 4 new images and pass them back with a new form (ProcessedImageForm) allowing them to choose one of the generated images
The user has now submitted the final form along with the original image, and the chosen processed image - save it all
This code is a bit messy but it gives you the idea. You would need to write two Forms yourself, along with probably a Model to represent the image and the processed/chosen image
from PIL import Image
def view(self, request):
if request.method=='POST':
# We check whether the user has just submitted the initial image, or is
# on the next step i.e. chosen a processed image.
# 'is_processing_form' is a hidden field we place in the form to differentiate it
if request.POST.get('is_processing_form', False):
form = ProcessedImageForm(request.POST)
if form.is_valid():
# If you use a model form, simply save the model. Alternatively you would have to get the data from the form and save the images yourself.
form.save()
return HttpResponseRedirect('/confirmation/')
elif form.POST.get('is_initial_form', False) and form.is_valid():
form = InitialImageForm(request.POST)
if form.is_valid():
# Get the image (it's not saved yet)
image = Image.open(form.cleaned_data.get('image').url)
processed_list = []
# Create your 4 new images and save them in list
...
# You can show a template here that displays the 4 choices
return direct_to_template(request,template = 'pick_processing.html',extra_context = { 'image' : image, 'processed_list':processed_list })
else:
# When the user arrives at the page, we give them a form to fill out
form = ImageSubmitForm()
return direct_to_template(request,template = 'upload_image.html',extra_context = { 'form' : form })
I have this code to submit the form:
form = form_class(request.POST, request.FILES, instance=object)
if form.is_valid():
form.save()
image_path = models.ImageField(upload_to='books/')
Currently books/book1/jpg gets inserted into that field.
But I want that the image should be renamed to modelName_pk.jpg and that value gets saved in image_path
How can I achieve that?
Each of the uploaded files are saved to request.FILES which are instances of UploadedFile, so, using the name of the image field in the form you can access it: request.FILES['imagename'].name.
Then you can save the model instance as usual. Hope this helps someone else because this is a very old question