I have a form where a user can submit some text, which then gets saved to the model TextSubmission.
However, it only saves when I take out instance=request.user from views.py and it does not save with the instance of the user who submitted it.
If I leave instance=request.user in, the request.POST object does not get saved but does get posted. (I can see from print(request.POST)
Can someone help me figure out why this is and how to get the text to save along with the user?
models.py
class TextSubmission(models.Model):
text_submission = models.CharField(max_length=50, null=True, blank=True)
user = models.OneToOneField(User, on_delete=models.CASCADE)
class Meta:
ordering = ['text_submission']
def __str__(self):
return self.text_submission
forms.py
class TextSubmissionForm(forms.ModelForm):
class Meta:
model = TextSubmission
fields = ['text_submission']
views.py
def profile_view(request):
if request.method == 'POST':
p_form = ProfileUpdateForm(request.POST, instance=request.user)
t_form = TextSubmissionForm(request.POST, request.FILES, instance=request.user.profile)
if p_form.is_valid() and t_form.is_valid():
p_form.save()
t_form.save()
messages.success(request, f'Your account has been updated!')
return redirect('profile')
else:
p_form = ProfileUpdateForm(instance=request.user.profile)
t_form = TextSubmissionForm(instance=request.user.textsubmission)
context = {
'p_form': p_form,
't_form': t_form,
}
return render(request, 'profile.html', context)
profile.html
<form method="POST" enctype="multipart/form-data">
<div class="profile-form">
{{ p_form }}
{{ t_form }}
</div>
<button type="submit" value="submit">Update Profile</button>
</form>
You're passing the TextSubmission form an instance of User, when you want to be passing it an instance of TextSubmission. Try
try:
t_form = TextSubmissionForm(request.POST, instance=request.user.textsubmission)
except TextSubmission.DoesNotExist:
t_form = TextSubmissionForm(request.POST)
Override the __init__() method and save() method of the form as
class TextSubmissionForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user')
super().__init__(*args, **kwargs)
def save(self, commit=True):
self.instance.user = self.user
return super().save(commit)
class Meta:
model = TextSubmission
fields = ['text_submission']
Then, in your views,
def profile_view(request):
if request.method == 'POST':
u_form = UserUpdateForm(request.POST) # change is here
t_form = TextSubmissionForm(request.POST, user=request.user) # change is here
if u_form.is_valid() and t_form.is_valid():
u_form.save()
t_form.save()
messages.success(request, f'Your account has been updated!')
return redirect('profile')
else:
u_form = UserUpdateForm() # change is here
t_form = TextSubmissionForm() # change is here
context = {
'u_form': u_form,
't_form': t_form,
}
return render(request, 'profile.html', context)
Reference
Passing a user, request to forms
The instance require the instance of the referenced model in form definition. In your case it is TextSubmission, so you have first get object of this model and then pass it to the TextSubmissionForm
user = User.objects.get(id = request.user)
txtSubmsn = TextSubmission.objects.get(user)
u_form = TextSubmissionForm(instance=txtSubmsn)
Same case for UserUpdateForm
Related
I'm trying to create a blog model but the form data is not being saved in the database after submitting the form.
views.py
def postsform(request):
if request.method == "POST":
form = BlogForm(request.POST)
if form.is_valid():
form.save()
return redirect('blog')
else:
form = BlogForm()
messages.warning(request, "Opps! Something went wrong.")
return render(request, 'blog/postform.html', {'form':form})
else:
form = BlogForm()
return render(request, 'blog/postform.html', {'form':form})
forms.py
from django_summernote.widgets import SummernoteWidget
class BlogForm(ModelForm):
class Meta:
model = BlogPost
widgets = {
'blog': SummernoteWidget(),
}
fields = ['title', 'featureImg', 'blog', 'meta_description', 'keyword', 'author']
models.py
class BlogPost(models.Model):
title = models.CharField(max_length=999)
featureImg = ProcessedImageField(upload_to = 'blog/', format='JPEG',options={'quality':60}, null=True)
slug = models.CharField(max_length=999, blank=True,null= True)
blog = models.TextField()
meta_description = models.TextField()
keyword = models.TextField()
author = models.CharField(max_length=255)
created_on = models.DateField(auto_now_add=True)
updated_on = models.DateField(auto_now=True)
def save(self, *args, **kwargs):
if BlogPost.objects.filter(title=self.title).exists():
extra = str(randint(1, 1000000))
self.slug = slugify(self.title) + "-" + extra
else:
self.slug = slugify(self.title)
super(BlogPost, self).save(*args, **kwargs)
html
<form method="POST">
{% csrf_token %}
{{form.as_p}}
<button type="submit">Publish</button>
</form>
I've tried finding where I made the mistake but couldn't find it. After submitting the form the warning message pops up and the form doesn't get submitted.
You should not construct a new form, since then it will not render the errors. Likely you did not pass request.FILES, and the enctype="…" [mdn-doc] is also missing in the <form> tag:
def postsform(request):
if request.method == 'POST':
form = BlogForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('blog')
else:
# no new BlogForm
messages.warning(request, 'Oops! Something went wrong.')
else:
form = BlogForm()
return render(request, 'blog/postform.html', {'form': form})
and in the HTML form:
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{form.as_p}}
<button type="submit">Publish</button>
</form>
When I use single form there is no problem but when I used multi-forms in a class based view it's getting validation failed with image field. I tried to fix with previous solution provided in stack overflow but couldn't able to solve it.
views.py
codes in views
class ProfileEditView(View):
profile_class = ProfileForm
profile_image_class = ProfileImageForm
template_name = 'user/profile_edit.html'
def get(self,request,pk):
if pk:
user = User.objects.get(pk=pk)
profile = Profile.objects.get(user = user)
profile_image = ProfileImage.objects.get(user = user)
profile_form = self.profile_class(instance = profile)
profile_image_form = self.profile_image_class(instance = profile_image)
context = {
'profile_form':profile_form,
'profile_image_form':profile_image_form
}
return render(request, self.template_name, context)
else:
profile_form = self.profile_class(None)
profile_image_form = self.profile_image_class(None)
context = {
'profile_form':profile_form,
'profile_image_form':profile_image_form
}
return render(request, self.template_name, context)
def post(self,request,pk=None, **kwargs):
profile_form = self.profile_class(request.POST,instance=Profile())
profile_image_form = self.profile_image_class(request.POST,instance=ProfileImage())
if profile_image_form.is_valid(): #and profile_image_form.is_valid():
profile = profile_form.save(commit=False)
profile_image = profile_image_form.save(commit=False)
profile.user = self.request.user
profile_image.user = self.request.user
profile.save()
profile_image.save()
return redirect('music:album_list')
context = {
'profile_form':profile_form,
'profile_image_form':profile_image_form,
'error_message':'Something went wrong',
}
return render(request, self.template_name, context)
models.py
codes in model
def get_profile_upload_to(instance,filename):
new_filename = '{}.{}'.format(uuid4,filename.split('.')[-1])
return "profile/{}/{}".format(instance.user.id, new_filename)
class ProfileImage(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE)
image = models.ImageField(upload_to=get_profile_upload_to)
uploaded = models.DateTimeField(auto_now=True)
class Profile(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE)
bio = models.TextField(max_length=500,null=True, blank=True)
location = models.CharField(max_length=50,null=True, blank=True)
birth_date = models.DateField(null=True, blank=True)
email_confirmed = models.BooleanField(default=False)
form.py
codes in form.py
class ProfileImageForm(forms.ModelForm):
class Meta:
model = ProfileImage
fields = ['image']
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['birth_date']
profile_edit.html
code in html page.
<form method="post" enctype="multipart/form-data">{% csrf_token %}
{{error_message}}
{{profile_form}}
{{profile_image_form}}
<button type="submit">Submit</button>
</form>
Error code
when I print the form.is_valid I got below lines.don't know Why image field is validating false
[21/Oct/2019 08:24:24] "POST /user/profile/46/ HTTP/1.1" 200 2861
profile_form is : True
profile_image_form is : False
profile_form is : <bound method BaseForm.is_valid of <ProfileForm bound=True, valid=True, fields=(birth_date)>>
profile_image_form is : <bound method BaseForm.is_valid of <ProfileImageForm bound=True, valid=False, fields=(image)>>
There are quite a few things wrong here. For example, your profile_form doesn't even seem to be a form.
But your immediate error is that you're not passing the FILES data to the image form. It should be:
profile_image_form = self.profile_image_class(request.POST, request.FILES)
(Note there's no point passing an empty instance, just leave that out.)
Also make sure that in your template the form has the right enctype:
<form action="" method="post" enctype="mulitpart/form-data">
I'm trying to user Django model formset factory to render a template where a user can add images and change the images they have uploaded(very similar to what can be done in the admin). I currently can render the template and its correct fields to the template. What I cannot do is have the user preselected(want currently logged in) and when I refresh the page the image will be posted again(not sure if this is preventable). Below is my code. Thanks!
Model:
class Image(models.Model):
user = models.ForeignKey(User)
image = models.ImageField(upload_to=content_file_name, null=True, blank=True)
link = models.CharField(max_length=256, blank=True)
Form:
class ImageForm(forms.ModelForm):
image = forms.ImageField(label='Image')
class Meta:
model = Image
fields = ('image',
'link',
)
View:
#login_required
def register(request):
user_data = User.objects.get(id=request.user.id)
ImageFormSet = modelformset_factory(Image,
fields=('user', 'image', 'link'), extra=3)
if request.method == 'POST':
print '1'
formset = ImageFormSet(request.POST, request.FILES, queryset=Image.objects.all())
if formset.is_valid():
formset.user = request.user
formset.save()
return render(request, 'portal/register.html', {'formset': formset, 'user_data': user_data})
else:
print '2'
formset = ImageFormSet(queryset=Image.objects.all())
return render(request, 'portal/register.html', {'formset': formset, 'user_data': user_data})
Template:
<form id="" method="post" action=""
enctype="multipart/form-data">
{% csrf_token %}
{{ formset.management_form }}
{% for form in formset %}
{{ form }}
{% endfor %}
<input type="submit" name="submit" value="Submit" />
let me explain the way you can do it.
MODELS
from django.utils.text import slugify
from django.db import models
from custom_user.models import AbstractEmailUser
# User model
class UserModel(AbstractEmailUser):
full_name = models.CharField(max_length=255)
def __str__(self):
return str(self.id)
# Function for getting images from instance of user
def get_image_filename(instance, filename):
title = instance.id
slug = slugify(title)
return "user_images/%s-%s" % (slug, filename)
# Save images with user instance
class UserImages(models.Model):
user = models.ForeignKey('UserModel', db_index=True, default=None)
image = models.ImageField(upload_to=get_image_filename, verbose_name='Image', db_index=True, blank=True, null=True)
In forms it's a just a two form, one for model User, other for UserImages model.
# Images forms
class ImageForm(forms.ModelForm):
image = forms.ImageField(label='Image', required=False)
class Meta:
model = UserImages
fields = ('image',)
# User form
class UserForm(forms.ModelForm):
full_name = forms.CharField(required=True)
class Meta:
model = UserModel
fields = ('full_name','email','password',)
And in Views for post you can do something like this
# View
from models import *
from forms import *
#csrf_protect
def post_view(request):
template = 'some_template.html'
ImageFormSet = modelformset_factory(UserImages, form=ImageForm, extra=15)
if request.method == 'POST':
user_form = UserForm(request.POST, prefix='form1')
formset = ImageFormSet(request.POST, request.FILES, queryset=UserImages.objects.none(), prefix='form2')
if user_form.is_valid() and formset.is_valid():
# Save User form, and get user ID
a = user_form.save(commit=False)
a.save()
images = formset.save(commit=False)
for image in images:
image.user = a
image.save()
return HttpResponseRedirect('/success/')
else:
user_form = UserForm(prefix='form1')
formset = ImageFormSet(queryset=UserImages.objects.none(), prefix='form2')
return render(request, template, {'form_user':user_form,'formset':formset})
In template you are doing the right thing.
I am using a Form to Upload a picture via dropzone.js. The picture gets saved and uploaded fine. But it's creating a new model with empty fields.. I want to save the picture to that existing model I'm referencing to. But I can't seem to find a solution to reference it. Here's my code:
views.py (I guess the error is in here)
def client_view(request, pk):
client = get_object_or_404(Client, pk=pk)
if request.method == 'POST':
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
picture = form.save()
else:
form = UploadFileForm()
data = {'form': form}
return render_to_response('client_view.html', locals(), RequestContext(request), {"img": picture})
return render(request, 'client_view.html', {'client': client})
urls.py
url(r'^client/(?P<pk>\d+)/$', client_view, name='client_view'),
forms.py
class UploadFileForm(forms.ModelForm):
class Meta:
model = Client
fields = ('image',)
models.py
class Client(models.Model):
customer_nr = models.IntegerField(null=True)
last_name = models.CharField(max_length=30, null=True)
first_name = models.CharField(max_length=30, null=True)
birthdate = models.DateField(null=True)
address = models.CharField(max_length=50, null=True)
image = models.ImageField("Image", upload_to='files/%Y/%m', null=True, blank=True)
client_view.html
<form action="{% url 'client_view' pk=client.pk %}" class="dropzone" id="myDropzone">
{% csrf_token %}
<div class="fallback">
<input name="file" type="file" multiple/>
</div>
</form>
<script type="text/javascript">
Dropzone.options.myDropzone = {
paramName: "image",
autoProcessQueue : true,
parallelUploads: 1,
init: function() {
this.on("success", function(file, responseText) {
console.log(responseText);
});
}
};
</script>
Any help would be really appreciated! Thanks!
The Django specified way is to instantiate the ModelForm using an existing model:
In your views.py:
client = get_object_or_404(Client, pk=pk)
if request.method == 'POST':
form = UploadFileForm(request.POST, request.FILES, client)
if form.is_valid():
picture = form.save()
else:
form = UploadFileForm(client)
For a POST request, this will automatically save the image to the correct model.
For a GET request, this will attach the image currently associated with the model, if any, to the form field before it is sent to be rendered on a browser.
In my opinion, don't use
form.save()
Because it is creating a new instance in the model. Rather I would like to suggest something like-
def client_view(request, pk):
client = get_object_or_404(Client, pk=pk)
if request.method == 'POST':
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
client.image = form.cleaned_data.get('image', None)
picture = client.save()
else:
form = UploadFileForm()
data = {'form': form}
return render_to_response('client_view.html', locals(), RequestContext(request), {"img": picture})
return render(request, 'client_view.html', {'client': client})
Building a form to let users submit posts as well as an image. My model has a text field, image, publish date, and author field. Publish Date and Author auto save without any user input. It works on the admin side with admin.py. However, when displaying this through a view, it is not working. Why?
#models.py
class Question(models.Model):
text = models.CharField(max_length = 500)
image = models.ImageField(upload_to = 'movie_poster')
pub_date = models.DateTimeField(auto_now_add = True)
author = models.ForeignKey(User)
def __unicode__(self):
return self.title
class QuestionForm(ModelForm):
class Meta:
model = Question
exclude = ('author', 'pub_date')
#views.py
def add_question(request):
def save_model(self, request, obj, form, change):
obj.author = request.user
obj.save()
if request.method == "POST":
form = QuestionForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return HttpResponseRedirect("/home/")
else:
form = QuestionForm
return render_to_response("qanda/add_question.html", {'form': form}, context_instance = RequestContext(request))
#add_question.html
{% block content %}
<div class="loginform">
<h1> Add Movie:</h1>
<form enctype = "multipart/form-data" action = "" method = "post">{% csrf_token %}
{{ form.as_p }}
<input type = "submit" value = "Add" />
<input type = "hidden" name = "next" value = "{{ next|escape }}" />
</form>
{% endblock %}
save_model is not magic that works everywhere just by being there. We'll need to customize the form:
class QuestionForm(ModelForm):
def save(self, user=None, force_insert=False, force_update=False, commit=True):
q = super(QuestionForm, self).save(commit=False)
q.author = user
if commit:
q.save()
return q
class Meta:
model = Question
exclude = ('author', 'pub_date')
def add_question(request):
if request.method == "POST":
form = QuestionForm(request.POST, request.FILES)
if form.is_valid():
form.save(user=request.user)
return HttpResponseRedirect("/home/")
else:
form = QuestionForm()
return render_to_response("qanda/add_question.html", {'form': form}, context_instance=RequestContext(request))