django - image isn't saving, all other fields are - django

When I update the user profile via the view everything is saving to the db except the image. The forms are validating but image isn't being saved. I can log in the admin portal and successfully add an image to an existing instance. I assume my problem lies in my html template but I can't figure out what it is.
**Btw I've read multiple similiar post but none I believe addresses my issue.
form.py
class EditUserForm(forms.ModelForm):
template_name='/something/else'
class Meta:
model = User
fields = (
'email',
'first_name',
'last_name',
)
class EditProfileForm(forms.ModelForm):
template_name='/something/else'
class Meta:
model = UserProfile
fields = (
'description',
'city',
'website',
'phone',
'image',
)
views.py
#transaction.atomic
def edit_profile(request):
if request.method == 'POST':
form = EditUserForm(request.POST, instance=request.user)
form2 = EditProfileForm(request.POST, instance=request.user.userprofile)
if form.is_valid() and form2.is_valid():
form.save()
form2.save()
return redirect(reverse('accounts:view_profile'))
else:
form = EditUserForm(instance=request.user)
form2 = EditProfileForm(instance=request.user.userprofile)
args = {'form': form, 'form2':form2}
return render(request, 'accounts/edit_profile.html', args)
models.py
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
description = models.CharField(max_length=100, default='')
city = models.CharField(max_length=100, default='')
website = models.URLField(default='')
phone = models.IntegerField(default=0)
image = models.ImageField(upload_to='profile_image', blank=True)
def __str__(self):
return self.user.username
edit_profile.html
<div class="container">
{% if form.errors %}
<ol>
{% for field in form %}
<H3 class="title">
<p class="error"> {% if field.errors %}<li>{{ field.errors|striptags }}</li>{% endif %}</p>
</H3>
{% endfor %}
</ol>
{% endif %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
{{ form2.as_p }}
<button type="submit">Submit</button>
</form>
<br>
</div>

If you are uploading files, you must instantiate the form with request.POST and request.FILES.
form2 = EditProfileForm(request.POST, request.FILES, instance=request.user.userprofile)
See the docs on file uploads for more info.

Related

Django how to add a form to a DetailView with FormMixin

I am attempting to add a form for comments to a DetailView. The DetailView displays notes for specific projects. So the comments have a foreign key that is the specific note and the note has a foreign key for the specific project.
I am attempting to use FormMixin with DetailView. So far I have not bee successful. Currently I can get the form to display but it does not save and in the terminal I see the following error Method Not Allowed (POST): /projects/project/1/note/1/
I can get these to work separately but not with the form in the DetailView.
Here are my models:
class ProjectNotes(models.Model):
title = models.CharField(max_length=200)
body = tinymce_models.HTMLField()
date = models.DateField(auto_now_add=True)
project = models.ForeignKey(Project, default=0, blank=True, on_delete=models.CASCADE, related_name='notes')
def __str__(self):
return self.title
class ProjectNoteComments(models.Model):
body = tinymce_models.HTMLField()
date = models.DateField(auto_now_add=True)
projectnote = models.ForeignKey(ProjectNotes, default=0, blank=True, on_delete=models.CASCADE, related_name='comments')
The View:
class ProjectNotesDetailView(DetailView, FormMixin):
model = ProjectNotes
id = ProjectNotes.objects.only('id')
template_name = 'company_accounts/project_note_detail.html'
comments = ProjectNotes.comments
form_class = NoteCommentForm
def form_valid(self, form):
projectnote = get_object_or_404(ProjectNotes, id=self.kwargs.get('pk'))
comment = form.save(commit=False)
comment.projectnote = projectnote
comment.save()
return super().form_valid(form)
def get_success_url(self):
return reverse('project_detail', args=[self.kwargs.get('pk')])
The form:
class NoteCommentForm(forms.ModelForm):
class Meta:
model = ProjectNoteComments
fields =['body',]
widgets = {
'body': forms.TextInput(attrs={'class': 'form-control'})
}
The template:
% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<div class="section-container container">
<div class="project-entry">
<h2>{{ projectnotes.title }}</h2>
<p>{{ projectnotes.body | safe }}</p>
</div>
<div><b>Comments on {{projectnotes.title}}</b></div>
{% if projectnotes.comments.all %}
{% for comment in projectnotes.comments.all %}
<div class="notecomments" style="padding: 10px;">
{{ comment.body | safe }}
</div>
{% endfor %}
{% else %}
<p>No comments have been have been added yet.</p>
{% endif %}
<h2>add note</h2>
<h1>Add Comment</h1>
<form action="" method="post">
{% csrf_token %}
{{ form.media }}
{{ form|crispy }}
<input type="submit" value="save">
</form>
{% endblock content %}
Try to change the order between DetailView and FormMixin in ProjectNotesDetailView then implement the post method (enabled by the `FormMixin):
class ProjectNotesDetailView(FormMixin, DetailView):
model = ProjectNotes
id = ProjectNotes.objects.only('id')
template_name = 'company_accounts/project_note_detail.html'
comments = ProjectNotes.comments
form_class = NoteCommentForm
def form_valid(self, form):
projectnote = get_object_or_404(ProjectNotes, id=self.kwargs.get('pk'))
comment = form.save(commit=False)
comment.projectnote = projectnote
comment.save()
return super().form_valid(form)
def get_success_url(self):
return reverse('project_detail', args=[self.kwargs.get('pk')])
def post(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return HttpResponseForbidden()
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
Check how to use formmixin with detailview (documentation).

Django pass PK from post to next page

I currently have a blog post feature on my site. I want users to be able to create sub posts
(build log ) off of the main posts.
I created this model with a FK to the Post model
class BuildLog(models.Model):
title = models.CharField(max_length=100)
content = RichTextUploadingField(blank=True, null=True)
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey('Post', on_delete=models.CASCADE)
I then added a button on the post page and created a form. Everything is working as expected but when I submit the form I get the error
null value in column "post_id" of relation "blog_buildlog" violates not-null constraint
If I understand the error correctly it is saying I am not passing the PK of the post into the form.
Views
def DetailPostView(request, pk):
model = Post
post = Post.objects.get(pk=pk)
form = CommentForm
comments = Comment.objects.filter(post=post)#.order_by('-create')
if request.method == 'POST':
# A comment was posted
comment_form = CommentForm(data=request.POST)
if comment_form.is_valid():
new_comment = comment_form.save(commit=False)
new_comment.author = request.user
new_comment.post = post
new_comment.save()
context = {
'post':post, 'form':form, 'comments':comments
}
return render(request, 'blog/post_detail.html', context)
class BuildLogView(LoginRequiredMixin, CreateView):
model = BuildLog
form_class = BuildLogForm
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
def save(self, *args, **kwargs):
super(BuildLog,self).save(*args, **kwargs)
Url
path('build-log-form/', BuildLogView.as_view(), name='build-log-form'),
path('post/<int:pk>/', views.DetailPostView, name='post-detail')
How do I go about passing the post PK into the next view?
forms.py
class BuildLogForm(ModelForm):
class Meta:
model = BuildLog
fields = ['title' , 'content']
html
{% extends "blog/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="content-section">
<form method="post" enctype="multipart/form-data" id="PostForm" novalidate>
<!--id="modelForm"-->
{% csrf_token %}
<fieldset class="django-ckeditor-widget">
<legend class="border-bottom mb-4">New Build Log</legend>
{{ form.title | as_crispy_field }}
{{ form.media }}
{{ form.content | as_crispy_field | safe}}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Post</button>
</div>
</form>
</div>
{% endblock content %}
Just incase their is any confusing It goes Post --> BuildLog(subpost)

NOT NULL constraint failed: social_media_app_blogcomment.user_id

I'm making this comment system for my blogs.. I already made the model, the ModelForm and the view to display the comments and the blog. I'm just really confused how to save the comments related to a specific blog. I tried to save the comments with a view but I face an IntegrityError. A little help would be appreciated.
Here's my views.py:
#login_required #View to show the blogs and comments related to it
def readblog(request, blog_pk):
Blog = get_object_or_404(blog, pk=blog_pk)
return render(request, 'social_media/readblog.html', {'Blog':Blog,'Form':CommentForm()})
#login_required #view to save the comments
def commentblog(request,blog_pk):
Blog = get_object_or_404(blog,pk=blog_pk)
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
Form = form.save(commit=False)
Form.Blog = Blog
Form.save()
return redirect('usersfeed')
Urls.py:
path('commentblog/<int:blog_pk>', views.commentblog, name='commentblog'),
path('readblog/<int:blog_pk>', views.readblog, name='readblog'),
HTML Page to write and save comments (along with the blog):
{{ Blog.title }}
<br>
{{ Blog.text }}
<br>
{% if Blog.image %}
<img src="{{ Blog.image.url }}" alt="">
{% endif %}
<br>
<form action="{% url 'commentblog' Blog.id %}" method="post">
{% csrf_token %}
{{ Form.as_p }}
<button type="submit">Comment!</button>
</form>
{% for i in Blog.BlogComment.all %}
{{ i.comment }}
<b>user:{{ i.user }}</b>
<br>
{% endfor %}
Comment's model:
class BlogComment(models.Model): # --run-syncdb <- (Research about this!)
user = models.ForeignKey(User, on_delete=models.CASCADE)
comment = models.CharField(max_length=250, null=True)
blog = models.ForeignKey(blog, related_name='BlogComment', on_delete=models.CASCADE, blank=True, null=True)
def __str__(self):
return self.comment
Forms.py:
class CommentForm(forms.ModelForm):
class Meta:
model = BlogComment
fields = ['comment']
You need to add the user since it's a not null field in your model:
def commentblog(request,blog_pk):
blog_obj = get_object_or_404(blog,pk=blog_pk)
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
form_obj = form.save(commit=False)
form_obj.blog = blog_obj
# add user instance
form_obj.user = request.user
form_obj.save()
return redirect('usersfeed')

KeyError 'image' formsets

It's my first time with formsets / images and this is my error:
KeyError at /houses/new/
'image'
This is my code:
models.py
class House(models.Model):
user = models.ForeignKey(User, related_name='houses', on_delete=models.CASCADE)
address = models.CharField(max_length=500)
type = models.CharField(default='House', max_length=100)
stories = models.IntegerField()
square_feet = models.IntegerField()
description = models.TextField()
# Class is for the houses images
class Image(models.Model):
house = models.ForeignKey(House, default=None, related_name="images", on_delete=models.CASCADE)
image = models.ImageField(verbose_name='image')
forms.py
# This is the blueprint for House forms
class AlbumForm(forms.ModelForm):
address = forms.CharField(label="Address:")
type = forms.CharField(label="Type of House (House, Condo, Cottage):")
stories = forms.IntegerField(label="House Stories:")
square_feet = forms.IntegerField(label='Square Feet:')
class Meta:
model = House
fields = ['address', 'type', 'stories', 'square_feet', 'description']
# This is the form for images
class ImageForm(forms.ModelForm):
image = forms.ImageField(label='Image')
class Meta:
model = Image
fields = ('image',)
views.py
def post_house(request):
ImageFormSet = modelformset_factory(Image, form=ImageForm, extra=10)
if request.method == 'POST':
house_form = AlbumForm(request.POST)
formset = ImageFormSet(request.POST, request.FILES, queryset=Image.objects.none())
if house_form.is_valid() and formset.is_valid():
post_form = house_form.save(commit=False)
post_form.user = request.user
post_form.save()
for form in formset.cleaned_data:
image = form['image']
photo = Image(house=post_form, image=image)
photo.save()
messages.success(request, "New house listing success")
house = post_form
return redirect('houses:details', house_id=house.pk)
else:
return render(request, 'login.html')
else:
house_form = AlbumForm()
formset = ImageFormSet(queryset=Image.objects.none())
return render(request, 'houses/house_form.html', {'house_form': house_form, 'formset': formset})
house_form.html
{% extends 'base.html' %}
{% block content %}
<br>
<div class="container">
<h4>Post a New Home</h4>
<form id="post_form" method="post" action="" enctype="multipart/form-data">
{% csrf_token %}
{{house_form}}
{{ formset.management_form }}
{% for form in formset %}
{{ form }} <br>
{% endfor %}
<input type="submit" name="submit" class="btn btn-success" value="Submit" />
</form>
</div>
{% endblock %}
It must be some relation between my form key and what each form in formset is taking. That being said they're both 'image' so I don't see the problem. Please let me know if you got an idea. Thanks a ton!
Try the below code,
if formset.is_valid():
for form in formset:
data = form.cleaned_data
image = data.get('image')
photo = Image(house=post_form, image=image)
photo.save()

Can't get data in edit form

I'm trying to create an edit form for existing users, I have the User model and I associated to it a profile.
The problem is that the fields of profile are empty in the rendered html, however when I created a new user I filled these fields, and when I enter to administration I find the fields are filled.
models.py
from django.db import models
from django.contrib.auth.models import User
class Profile(models.Model):
DEPARTMENT_CHOICES = (('MI', 'Math et info'),
('ST', 'Science et Tech'),
('SM', 'Science de la matiere'))
user = models.OneToOneField(User, on_delete=models.CASCADE)
teacher = models.BooleanField(default=False)
description = models.TextField(blank=True)
department = models.CharField(max_length=35, choices=DEPARTMENT_CHOICES, blank=True)
picture = models.ImageField(upload_to='profile-images', blank=True)
def __str__(self):
return self.user.username
views.py
def profile_view(request):
if request.method == 'POST':
user_form = EditUserForm(request.POST, instance=request.user)
ins = Profile.objects.get(pk=5)
profile_form = EditProfileForm(request.POST, request.FILES, instance=ins)
if user_form.is_valid() and profile_form.is_valid():
user = user_form.save()
user.save()
profile = profile_form.save(commit=False)
profile.user = user
if 'picture' in request.FILES:
profile.picture = request.FILES['picture']
profile.save()
return redirect(home)
else:
user_form = EditUserForm(instance=request.user)
profile_form = EditProfileForm(request.FILES, instance=request.user)
return render(request, 'account/profile.html', {'user_form': user_form,
'profile_form': profile_form})
forms.py
class EditProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('description', 'department', 'picture', )
class EditUserForm(forms.ModelForm):
class Meta:
model = User
fields = ('username', 'email', )
profile.html
{% extends 'manhal/base.html' %}
{% load staticfiles %}
{% load crispy_forms_tags %}
{% block content %}
<div class="col-md-6">
<form method="post" enctype="multipart/form-data" action="{% url 'profile' %}" class="form-horizontal">{% csrf_token %}
<fieldset>
<legend>User Profile</legend>
{{ user_form|crispy }}
{{ profile_form|crispy}}
<input type="submit" value="Save" class="btn btn-primary">
</fieldset>
</form>
</div>
{% endblock %}
First, your if/else block is checking if the request is a POST. Since the else block is not a POST, you do not want to pass any POST data into your form. This will make the form think it's bound with no data.
Also, it looks like you are passing the request.user to your ProfileForm as the instance, but the model on the ProfileForm meta class is expecting a Profile object.
Can you fix those two things and see if it works or not? If it doesn't work, please post some more code (like your templates).