Django - How to Save ImageField Even if Form is Not Valid - django

I have a fairly standard model and form. I have some mandatory fields with an ImageField. When the user choose an image and doesn't field the mandatory fields, the image isn't saved and the user needs to 're-upload' it again.
As the row in the database already exists (because this form is part of a 'wizard'), I don't mind saving the picture in the database even if all the form isn't valid with all the mandatory data.
This is what I have right now in my view, which works when you fill all the mandatory fields:
def my_view(request):
instance = MyModel.objects.get(user=request.user)
form = MyForm(instance=instance)
if request.POST:
form = MyForm(request.POST, request.FILES, instance=instance)
if form.is_valid():
new_instance = form.save(commit=False)
if request.FILES and request.FILES['photo']:
uploaded_photo = request.FILES['photo']
new_instance.photo = uploaded_photo
new_instance.save()
return HttpResponseRedirect(reverse('new_url'))
return render_to_response('current_template.html', locals(), context_instance=RequestContext(request))
Here's what I tried to save the picture in DB even if the other fields aren't filled, but I get the error Django Upload Error - Upload a valid image (either not an image or corrupted):
def my_view(request):
instance = MyModel.objects.get(user=request.user)
form = MyForm(instance=instance)
if request.POST:
form = MyForm(request.POST, request.FILES, instance=instance)
if request.FILES and request.FILES['photo']:
uploaded_photo = request.FILES['photo']
instance.photo = uploaded_photo
instance.save()
if form.is_valid():
new_instance = form.save()
return HttpResponseRedirect(reverse('new_url'))
return render_to_response('current_template.html', locals(), context_instance=RequestContext(request))
Here's my form (fairly simple):
class MyForm(ModelForm):
first_name = forms.CharField(label='First Name', max_length=50, required=True)
last_name = forms.CharField(label='Last Name', max_length=50, required=True)
photo = forms.ImageField(required=False)
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
class Meta:
model = MyModel
fields = ('first_name','last_name', 'photo')
Here's my model (again very simple):
class MyModel(models.Model):
first_name = models.TextField(max_length=50)
last_name = models.TextField(max_length=50)
photo = ImageField(upload_to=get_photo_path,null=True,blank=True)

This is how I made it work. Notice that there's no 'request.FILES' as a parameter for the constructor of the form in the 'else' when the form is not valid. This is what made Django display the error message.
if form.is_valid():
instance = form.save(commit=False)
if request.FILES and request.FILES['photo']:
instance = save_photo(instance, request.FILES['photo'])
instance.save()
return HttpResponseRedirect(reverse('url'))
else:
if request.FILES and request.FILES['photo']:
instance = save_photo(instance, request.FILES['photo'])
form = InstanceForm(request.POST, instance=instance)

Related

Populate custom field in Django form

I would like users to have the ability to update their email address. I created a profile that has fields, but the email address is in the users table. I created a form that adds a custom form field and it works for update. However, I can't find a way to pre-populate this field on a REQUEST.GET.
# forms.py
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('name', 'timezone')
class ProfileUpdateForm(ProfileForm):
email = forms.EmailField(max_length=254)
class Meta(ProfileForm.Meta):
fields = ProfileForm.Meta.fields + ('email',)
# views.py
#login_required
#require_http_methods(["GET","POST"])
def profile_update_view(request):
context = {}
# Get the logged in users profile
profile_object = Profile.objects.get(user=request.user.id)
if request.method == 'GET':
profile_form = ProfileUpdateForm(None, instance=profile_object)
context["form"] = profile_form
# how can I add User.objects.get(id=request.user.id).email to the custom field
if request.method == 'POST':
profile_form = ProfileUpdateForm(request.POST or None, instance=profile_object)
context["form"] = profile_form
if profile_form.is_valid():
try:
# email address exists
user = User.objects.get(email=profile_form.cleaned_data.get('email'))
messages.error(request, 'Failed profile update. Email address already exists.')
except:
# email address available
# get user object
user = User.objects.get(id=request.user.id)
user.email = profile_form.cleaned_data.get('email')
# update user object
user.save()
profile_form.save()
messages.success(request, 'Successful profile update.')
return render(request, "profile.html", context)
I tend to favour class-based views, and things like this are where they come into their own. The form:
class ProfileUpdateForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('name', 'timezone')
email = forms.EmailField(max_length=254) #add non-model form field
And a class-based view. Handle the initial value for email in get_initial(), and updating of self.request.user in form_valid():
class ProfileUpdateView( UpdateView):
model = Profile
form_class = ProfileUpdateForm
template_name = 'profile.html' # profiles/update_profile.html would be better
# other declarations ...?
def get_initial(self):
initial = super().get_initial()
initial['email'] = self.request.user.email
return initial
# #transaction.atomic might be a good idea
def form_valid(self, form):
new_email = form.cleaned_data['email']
user = self.request.user
if user.email != new_email: # don't do a pointless non-update save
user.email = new_email
user.save()
return super().form_valid( form) # will save the profile
# forms.py
def __init__(self, *args, **kwargs):
self.email = kwargs.pop("email")
super(ProfileUpdateForm, self).__init__(*args, **kwargs)
self.initial['email'] = self.email
# views.py
if request.method == 'GET':
profile_form = ProfileUpdateForm(None, instance=profile_object, email=request.user.email)
context["form"] = profile_form
if request.method == 'POST':
profile_form = ProfileUpdateForm(request.POST or None, instance=profile_object, email=request.POST.get('email'))
context["form"] = profile_form

Django Form : after correctly submitting form I got this: "this field is required"

I've a category and I've added a form for user in each category.
So I've two fields to fill and after filling them correctly I submit but the page reload, and nothing appears in my DB... only one error on Image field: This field required. I don't really know what's wrong here.
class Picture(models.Model):
catego = models.ForeignKey(Catego,on_delete=models.CASCADE,related_name="catego_pictures")
user = models.ForeignKey(User, blank=True, null=True,on_delete=models.CASCADE,related_name='user_pictures')
image = models.ImageField(upload_to='nutriscore/')
pictureoption = models.CharField(max_length=20,choices=Pictureoption.choices,default=Pictureoption.HOME,)
publishing_date = models.DateField(auto_now_add=True)
class CreatePictureForm(forms.ModelForm):
def __init__(self,*args,**kwargs):
super(CreatePictureForm, self).__init__(*args,**kwargs)
self.helper = FormHelper()
self.helper.form_method="post"
self.helper.layout = Layout(
Field("image",css_class="single-input"),
Field("pictureoption",css_class="single-input"),
)
self.helper.add_input(Submit('submit','Upload a pic',css_class="single-input textinput textInput form-control"))
class Meta:
model = Picture
fields = [
'image',
'pictureoption',
]
def __str__(self):
return self.catego.name
views.py
#login_required(login_url='/cooker/login')
def catego(request, slug):
catego = Catego.objects.get(slug=slug)
context = {
'catego': catego
}
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = CreatePictureForm(request.POST)
# check whether it's valid:
if form.is_valid():
form.instance.catego = self.object
form.instance.user = self.request.user
form.save()
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
else:
form = CreatePictureForm()
context['form'] = form # add `form` to the context
return render(request, 'post_catego.html', context)
here is the answer
#login_required(login_url='/cooker/login')
def catego(request, slug):
catego = Catego.objects.get(slug=slug)
context = {
'catego': catego
}
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = CreatePictureForm(request.POST, request.FILES)
# check whether it's valid:
if form.is_valid():
form.instance.catego = catego
form.instance.user = request.user
form.save()
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
else:
form = CreatePictureForm()
context['form'] = form # add `form` to the context
return render(request, 'post_catego.html', context)

Django formset: Unable to save

Trying to setup simple formset, but getting errors
'SWDataForm' object has no attribute 'save'
MODEL
class SWData(ValidateOnSaveMixin, models.Model):
model_serial = models.ForeignKey(SWInfo, related_name='serial_items', on_delete=models.SET_NULL, null=True)
hostname = models.CharField(max_length=20, default='', unique=True)
deployed = models.BooleanField()
class Meta:
verbose_name_plural = "SWDATA"
def __str__(self):
return "{0}".format(self.hostname)
VIEW
def display_data(request, data, **kwargs):
return render(request, 'web/posted-data.html', dict(data=data, **kwargs))
def swdata(request, *args, **kwargs):
template = 'web/swdata.html'
SWDataFormset = modelformset_factory(SWData, fields=('__all__'), extra=1)
formset = SWDataFormset(request.POST or None)
if request.method == 'POST':
print(formset.is_valid())
if formset.is_valid():
pprint(formset)
for form in formset.forms:
if form.is_valid():
try:
if form.cleaned_data.get('DELETE') and form.instance.pk:
form.instance.delete()
else:
instance = form.save(commit=False)
#instance.model_serial = model_serial
#print (instance.model_serial)
instance.save()
messages.success(request, "Successfully")
except formset.DoesNotExist:
messages.error(request, "Database error. Please try again")
#data = form.cleaned_data
#return display_data(request, data)
else:
formset = SWDataFormset(request.POST or None)
return render(request, template, {'formset': formset})
Remove the form and used the modelformset_factory, I was able to save only the last entry in the formset. How to loop thru each formset prefix and save each item?
I think that you are trying to save data form from a form, and you can't do that. You need to use the method create of your model. Something like SWDData.objects.create(form).
remove this line
instance = form.save(commit=False)
this line is needed when you wants to edit the form.
Now check if it works
Even I also got similar Problem

Multiple images in django form with multiupload

I need to add multiple images in django form to one model. I did a research and for form outside of django I try to setup django-multiupload.
My models.py:
class Profile(models.Model):
...
...
first = models.ImageField("first", upload_to='first')
second = models.ImageField("second", upload_to='second')
...
In forms.py:
class AddForm(forms.ModelForm):
first = MultiImageField(min_num=1, max_num=20)
second = MultiImageField(min_num=1, max_num=4)
In views.py:
class UploadView(FormView):
template_name = 'finder/submit.html'
form_class = AddForm
success_url = '/'
def form_valid(self, form):
for each in form.cleaned_data['first']:
Profile.objects.create(first=each)
for each in form.cleaned_data['second']:
Profile.objects.create(second=each)
return super(UploadView, self).form_valid(form)
And on submitting form this form creates multiple Profile objects with only first/second field filled.
How can I create only one model with remaining fields (other than first/second) and with multiple first/second fields?
It was my function-based view before adding multiupload but I couldn't make it work, maybe it's easier to change it somehow?
def add_form(request, *args, **kwargs):
if request.method == "POST":
form = AddForm(request.POST)
if form.is_valid():
profile = form.save(commit=False)
profile.save()
return redirect('/', pk=profile.pk)
else:
form = AddForm()
return render(request, 'finder/submit.html', {'form': form})
I have never used the Django-multiupload, but I happen to read some of the docs.
If you want to save multiple files for your user model, you may need to create another model for accommodating the files and add a Foreign Key towards the Profile model.
Remove the first and second fields from Profile model. It causes you to create multiple profiles with same data inorder to accomodate multiple images.
Simple example,
class Image(models.Model):
image = models.FileField()
profile = models.ForeignKey(Profile, related_name='images')
is_first = models.BooleanField(default=False)
is_second = models.BooleanField(default=False)
Then, edit the save method in form,
class AddForm(forms.ModelForm):
first = MultiImageField(min_num=1, max_num=20)
second = MultiImageField(min_num=1, max_num=4)
class Meta:
model = Profile
fields = (.......... 'first', 'second')
def save(self, commit=True):
first_images = self.cleaned_data.pop('first')
second_images = self.cleaned_data.pop('second')
instance = super(AddForm, self).save()
for each in first_images:
first = Image(image=each, profile=instance, is_first=True)
first.save()
for each in second_images:
second = Image(image=each, profile=instance, is_second=True)
second.save()
return instance
Then, on the views, edit the view,
class UploadView(FormView):
template_name = 'finder/submit.html'
form_class = AddForm
success_url = '/'
def form_valid(self, form):
instance = form.save(commit=False)
instance.user = self.request.user
instance.save()
return super(UploadView, self).form_valid(form)
Or in function based view,
def add_form(request, *args, **kwargs):
if request.method == "POST":
form = AddForm(request.POST)
if form.is_valid():
profile = form.save(commit=False)
profile.user = request.user
profile.save()
return redirect('/', pk=profile.pk)
else:
form = AddForm()
return render(request, 'finder/submit.html', {'form': form})

modelform fails is_valid w/o setting form.errors

I'm using a modelform to display only a subset of the fields in the model. When the form is submitted it fails form.is_valid() but form.errors is empty. I'd rather not display all my code here, but below is a sample:
Model and Form
class Videofiles(models.Model):
active = models.CharField(max_length=9)
filenamebase = models.CharField(max_length=180, primary_key=True, db_column='FilenameBase')
program = models.CharField(max_length=60, db_column='Program')
displayname = models.CharField(max_length=150, db_column='DisplayName')
description = models.TextField(db_column='Description', blank=True)
tagskeywords = models.TextField(db_column='TagsKeywords', blank=True)
class Meta:
db_table = u'legacyTable'
class VideoFilesForm(ModelForm):
filenamebase = forms.CharField(max_length=30)
displayname = forms.CharField(max_length=30)
description = forms.CharField(max_length=30, required=False)
tagskeywords = forms.CharField(max_length=60, required=False)
class Meta:
model=Videofiles
fields=['filenamebase','displayname','description','tagskeywords']
View
def editClip(request, clipId):
clip = Videofiles.objects.get(filenamebase=clipId)
form = VideoFilesForm(instance=clip)
if request.method == 'POST':
if 'save' in request.POST:
if form.is_valid():
form.save()
else:
print form.errors
return render_to_response('legacyDB/edit_clip.html',locals())
Your form is unbound, because you are not passing any data to it. Calling is_valid on an unbound form will always return False, with empty errors (see the form api docs).
Your view should be something like the following:
def editClip(request, clipId):
clip = Videofiles.objects.get(filenamebase=clipId)
if request.method == 'POST':
# create bound form with input from request.POST
form = VideoFilesForm(request.POST, instance=clip)
if 'save' in request.POST:
if form.is_valid():
form.save()
else:
print form.errors
else:
# create unbound form to display in template
form = VideoFilesForm(instance=clip)
return render_to_response('legacyDB/edit_clip.html',locals())