Adding an ImageField to already existing model in django - python

I have followed Django Girls tuorial http://tutorial.djangogirls.org/en/index.html and successfully created a blog. However I wanted to add an image field to my block,
Currently my models.py looks like:
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
class Post(models.Model):
author = models.ForeignKey(User)
title = models.CharField(max_length = 200)
text = models.TextField()
model_pic= models.ImageField(upload_to = 'blog/images', default='blog/images/already.png')
created_date = models.DateTimeField(default = timezone.now)
published_date = models.DateTimeField(blank = True, null =True)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.title
def approved_comment(self):
return self.comments.filter(approved_comment=True)
class Comment(models.Model):
post = models.ForeignKey('blog.Post', related_name='comments')
author = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
approved_comment = models.BooleanField(default=False)
def approve(self):
self.approved_comment = True
self.save()
def __str__(self):
return self.text
and my views.py looks like:
from django.shortcuts import render, get_object_or_404 ,redirect
from .models import Post, Comment
from django.utils import timezone
from .forms import PostForm, CommentForm
from django.contrib.auth.decorators import login_required
def post_list(request):
posts = Post.objects.filter(published_date__lte = timezone.now()).order_by('published_date')
return render(request, 'blog/post_list.html',{'posts' : posts})
def post_detail(request, pk):
post = get_object_or_404(Post, pk = pk)
return render(request, 'blog/post_detail.html',{'post':post})
#login_required
def post_new(request):
if request.method == "POST":
form = PostForm(request.POST)
if form.is_valid():
post = form.save(commit = False)
post.author = request.user
post.published_date = timezone.now()
post.save()
return redirect('blog.views.post_detail', pk=post.pk)
else:
form = PostForm()
return render(request, 'blog/post_edit.html', {'form': form})
#login_required
def post_edit(request, pk):
post = get_object_or_404(Post, pk=pk)
if request.method == "POST":
form = PostForm(request.POST, instance=post)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.published_date = timezone.now()
post.save()
return redirect('blog.views.post_detail', pk=post.pk)
else:
form = PostForm(instance=post)
return render(request, 'blog/post_edit.html', {'form': form})
#login_required
def post_draft_list(request):
posts = Post.objects.filter(published_date__isnull=True).order_by('created_date')
return render(request, 'blog/post_draft_list.html',{'posts':posts})
#login_required
def post_publish(request, pk):
post = get_object_or_404(Post, pk=pk)
post.publish()
return redirect('blog.views.post_detail', pk=pk)
#login_required
def post_remove(request, pk):
post = get_object_or_404(Post, pk=pk)
post.delete()
return redirect('blog.views.post_list')
def add_comment_to_post(request, pk):
post = get_object_or_404(Post, pk=pk)
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit = False)
comment.post = post
comment.save()
return redirect('blog.views.post_detail', pk=post.pk)
else:
form = CommentForm()
return render(request,'blog/add_comment_to_post.html', {'form': form})
#login_required
def comment_approve(request, pk):
comment = get_object_or_404(Comment, pk=pk)
comment.approve()
return redirect('blog.views.post_detail', pk=comment.post.pk)
#login_required
def comment_remove(request, pk):
comment = get_object_or_404(Comment, pk=pk)
post_pk = comment.post.pk
comment.delete()
return redirect('blog.views.post_detail', pk=post_pk)
and my forms.py appears like :
from django import forms
from .models import Post, Comment
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ('title', 'text')
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('author', 'text',)
Now Please guide me what changes should I make in model Post and how should I modify the view to get my image rendered and how should I make the template for same
I tried https://coderwall.com/p/bz0sng/simple-django-image-upload-to-model-imagefield but It didn't worked for me.
Any help will be highly appreciated.
Thank You.
Had you need any more information please comment it.

So, let's go.
When you set a ImageField(), you have a upload_to attribute. From the documentation:
A local filesystem path that will be appended to your MEDIA_ROOT setting to determine the value of the url attribute.
So you will have a function to determine a dynamic path where the image will be stored (read the link above for more information).
E.g.
class Post(models.Model):
author = models.ForeignKey(User)
title = models.CharField(max_length = 200)
text = models.TextField()
model_pic= models.ImageField(upload_to=upload_image, default='blog/images/already.png')
created_date = models.DateTimeField(default = timezone.now)
published_date = models.DateTimeField(blank = True, null =True)
def upload_image(self, filename):
return 'post/{}/{}'.format(self.title, filename)
Note that you don't include the model_pic in your fields of the PostForm. So it will not appear on the template.
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ('title', 'text', 'model_pic',)
Your view will change a little bit:
#login_required
def post_new(request):
if request.method == "POST":
form = PostForm(request.POST, request.FILES)
Once you're using an upload, you will use the request.FILES read more here, but let me quote this:
Note that FILES will only contain data if the request method was POST and the that posted to the request had enctype="multipart/form-data". Otherwise, FILES will be a blank dictionary-like object.
So on your template, your tag <form> will have a attribute enctype="multipart/form-data"
<form enctype="multipart/form-data" method="POST" action="">
{{ form.as_p }}
</form>
After all, you can retrieve this image to show in a page. Assuming that you have passed a post instance via context to the template:
<img src="{{ post.model_pic.url }}" alt="{{ post.title }}" />

Related

How to get the current logged in user? Django models

I want to get the current logged in user to this CreateForm form. Below Im giving my current code, here request.user is not giving me error
ValueError at /create-post/
Cannot assign "<SimpleLazyObject: <User: testuser>>": "Post.author" must be a "Author" instance.
models.py
class Author(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
rate = models.IntegerField(default=0)
class Post(models.Model):
title = models.CharField(max_length= 50)
overview = models.TextField()
body_text = RichTextUploadingField(null = True)
time_upload = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(Author, related_name='author', on_delete=models.CASCADE)
thumbnail = models.ImageField(upload_to = 'thumbnails')
publish = models.BooleanField()
categories = models.ManyToManyField(Categories)
read = models.IntegerField(default=0)
slug = models.SlugField(null= True, blank= True)
Forms.py
class CreateForm(ModelForm):
class Meta:
model = Post
fields = [
'title',
'overview',
'body_text',
'thumbnail',
'categories',
'publish',
]
Views.py
def create_post(request):
if request.method == 'POST':
form = CreateForm(request.POST, request.FILES)
if form.is_valid:
post = form.save(commit=False)
post.author= request.user
post.save()
return render(request, 'index.html')
else:
form = CreateForm()
return render(request, 'create_post.html', {'form': form})
As the error says, post.author expects an Author object, not the user. You thus retrieve this with:
from django.contrib.auth.decorators import login_required
#login_required
def create_post(request):
if request.method == 'POST':
form = CreateForm(request.POST, request.FILES)
if form.is_valid():
form.instance.author= request.user.author
form.save()
return render(request, 'index.html')
else:
form = CreateForm()
return render(request, 'create_post.html', {'form': form})
You should also call the is_valid() method [Django-doc], so form.is_valid().
Note: You can limit views to a view to authenticated users with the
#login_required decorator [Django-doc].
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
The author field on your Post model is a foreign key to Author model, and not user. You could replace post.author = request.user with:
if request.user.author:
post.author = request.user.author
else:
post.author = Author.objects.create(user=request.user)
The answer from Willem works if an Author instance was already created for the User, but will throw an exception if there's no Author object associated with that user.

ValueError at /adding-url-patterns-for-views

I am trying to allow user to post answer and comments to a particular bloq question
Q&A
Error displayed:
ValueError at /adding-url-patterns-for-views
Cannot assign "<SimpleLazyObject<django.contrib.auth.models.AnonymousUser object at 0x04A5B370>>": "PostAnswer.user" must be a "CustomUser" instance.
Here is my model
class PostQuestion(models.Model):
""" Model for posting questions """
title = models.CharField(max_length=100, unique=True)
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE, null=True, blank=True)
slug = models.SlugField(unique=True, null=True, max_length=250)
created_on = models.DateTimeField('date published',
auto_now_add=True)
text_content = models.TextField()
tags = TaggableManager()
class Meta:
ordering = ['-created_on']
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('detail_view', kwargs={'slug': self.slug})
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.title)
return super().save(*args, **kwargs)
class PostAnswer(models.Model):
""" Model for answering questions"""
question = models.ForeignKey(
PostQuestion,
on_delete=models.CASCADE,
related_name='comments',
)
text_content = models.TextField()
user = models.ForeignKey(
User,
on_delete=models.CASCADE,
)
approved_comment = models.BooleanField(default=False)
created_on = models.DateTimeField('published', auto_now_add=True)
class Meta:
ordering = ['created_on']
def approve(self):
self.approved_comment = True
self.save()
def __str__(self):
return 'comment by {} on {}'.format(self.user, self.question)
Here is my views.py on an app
class Index(ListView):
queryset = PostQuestion.objects.all()
template_name = 'index.html'
context_object_name = 'posts'
paginate_by = 10
def detail(request, slug):
post = get_object_or_404(PostQuestion, slug=slug)
comments = post.comments.all()
# comment posted
if request.method == 'POST':
form = CommentForm(data=request.POST)
if form.is_valid():
form.instance.question = post
form.instance.user = request.user
form.save()
return redirect('post_detail', slug=slug)
else:
form = CommentForm()
context = {'post': post,
'comments': comments,
'form': form}
return render(request, 'detail.html', context)
My App form:
class CommentForm(forms.ModelForm):
text_content = forms.CharField(widget=PagedownWidget())
class Meta:
model = PostAnswer
fields = ['text_content']
My urls.py view for the app
from .views import detail, ask, Index
from django.urls import path
urlpatterns = [
# path('<slug:slug>/comment', add_comment_to_post, name='add_comment_to_post'),
path('ask/', ask, name='ask'),
path('<slug:slug>', detail, name='detail_view'),
path('', Index.as_view(), name='index'),
]
Here is the html template for the comment
<!--------Display a form for users to add a new comment------->
<h3>Leave a comment</h3>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary btn-sm">Add Comment</button>
</form>
This is all of code that I think you need to know the problem. I don't know if the problem can be caused for database or why are not all instructions in the views.
If you can help me, thanks you in advance.
The name of the field is question, not post, so you should rewrite the logic to:
new_comment.question = post
This will however still raise an error, since the user field is not filled in either.
Using commit=False is however not a good idea, since then the form can no longer save many-to-many fields. As far as I can see, there are at the moment no many-to-many fields, but nevertheless, if you later add such fields, you have to rewrite the logic. You can improve the view to:
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect
#login_required
def detail(request, slug):
post = get_object_or_404(PostQuestion, slug=slug)
comments = post.comments.all()
if request.method == 'POST':
form = CommentForm(data=request.POST)
if form.is_valid():
form.instance.question = post
form.instance.user = request.user
form.save()
return redirect('page:detail_view', slug=slug)
else:
form = CommentForm()
context = {
'post': post,
'comments': comments,
'form': form
}
return render(request, 'detail.html', context)
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
Note: You can limit views to a view to authenticated users with the
#login_required decorator [Django-doc].

AttributeError: module 'django.contrib.auth.views' has no attribute 'add_comment_to_post'

I completed all the steps from the following website:
https://tutorial-extensions.djangogirls.org/en/homework_create_more_models/
everything seems correct, and for some reason the server cannot be run on cmd.
Below is the error displayed:
32\Scripts\mysite\urls.py", line 26, in <module>
path('post/<int:pk>/comment/', views.add_comment_to_post, name='add_comment_to_post'),
AttributeError: module 'django.contrib.auth.views' has no attribute 'add_comment_to_post'
Editing urls.py, views.py, forms.py was done several times, and also comment model was added to the database with: "python manage.py makemigrations blog", and "python manage.py migrate blog"
urls.py:
from django.contrib import admin
from django.urls import path, include
from django.contrib.auth import views
urlpatterns = [
path('admin/', admin.site.urls),
path('accounts/login/', views.LoginView.as_view(), name='login'),
path('accounts/logout/', views.LogoutView.as_view(next_page='/'), name='logout'),
path('', include('blog.urls')),
path('post/<int:pk>/comment/', views.add_comment_to_post, name='add_comment_to_post'),
path('comment/<int:pk>/approve/', views.comment_approve, name='comment_approve'),
path('comment/<int:pk>/remove/', views.comment_remove, name='comment_remove'),
views.py:
from django.shortcuts import render
from django.utils import timezone
from .models import Post, Comment
from .forms import PostForm, CommentForm
from django.shortcuts import render, get_object_or_404
from django.shortcuts import redirect
from django.contrib.auth.decorators import login_required
def post_list(request):
posts = Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date')
return render(request, 'blog/post_list.html', {'posts': posts})
def post_detail(request, pk):
post = get_object_or_404(Post, pk=pk)
return render(request, 'blog/post_detail.html', {'post': post})
#login_required
def post_new(request):
if request.method == "POST":
form = PostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.save()
return redirect('post_detail', pk=post.pk)
else:
form = PostForm()
return render(request, 'blog/post_edit.html', {'form': form})
#login_required
def post_edit(request, pk):
post = get_object_or_404(Post, pk=pk)
if request.method == "POST":
form = PostForm(request.POST, instance=post)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.save()
return redirect('post_detail', pk=post.pk)
else:
form = PostForm(instance=post)
return render(request, 'blog/post_edit.html', {'form': form})
#login_required
def post_draft_list(request):
posts = Post.objects.filter(published_date__isnull=True).order_by('created_date')
return render(request, 'blog/post_draft_list.html', {'posts':posts})
#login_required
def post_publish(request, pk):
post = get_object_or_404(Post, pk=pk)
post.publish()
return redirect('post_detail', pk=pk)
#login_required
def post_remove(request, pk):
post = get_object_or_404(Post, pk=pk)
post.delete()
return redirect('post_list')
def add_comment_to_post(request, pk):
post = get_object_or_404(Post, pk=pk)
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.post = post
comment.save()
return redirect('post_detail', pk=post.pk)
else:
form = CommentForm()
return render(request, 'blog/add_comment_to_post.html', {'form': form})
#login_required
def comment_approve(request, pk):
comment = get_object_or_404(Comment, pk=pk)
comment.approve()
return redirect('post_detail', pk=comment.post.pk)
#login_required
def comment_remove(request, pk):
comment = get_object_or_404(Comment, pk=pk)
comment.delete()
return redirect('post_detail', pk=comment.post.pk)
forms.py:
from django import forms
from .models import Post, Comment
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ('title', 'text',)
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('author', 'text',)
models.py:
from django.conf import settings
from django.db import models
from django.utils import timezone
class Post(models.Model):
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
published_date = models.DateTimeField(blank=True, null=True)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.title
def approved_comments(self):
return self.comments.filter(approved_comment=True)
class Comment(models.Model):
post = models.ForeignKey('blog.Post', on_delete=models.CASCADE, related_name='comments')
author = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
approved_comment = models.BooleanField(default=False)
def approve(self):
self.approved_comment = True
self.save()
def __str__(self):
return self.text
You've put the URL patterns in the wrong urls.py; you've put them in mysite/urls.py, the instructions say to use blog/urls.py.

Django duplicates my entry when I store something

When I try to save a post, the post is saved, but current user is not registered and the post is duplicated with a blank entry and the current user is not stored.
For adding the post I use not the admin app but a personal template and form.
See the problem:
This is my view code:
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required
from django.shortcuts import render, redirect
from .forms import NewAdminPostForm
from .models import Post, Category
# Create your views here.
def home(request):
posts = Post.objects.all()
categories = Category.objects.all()
posts_last = Post.objects.order_by('-created_at')[0:3]
return render(request, 'front/blog-list.html', {'posts': posts,
'categories': categories, 'posts_last': posts_last})
#login_required
def newadminpost(request):
if request.method == 'POST':
form = NewAdminPostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
Post.objects.create(
message=form.cleaned_data.get('message'),
category_id=post.category_id,
created_by=request.user
)
#post.save()
return redirect('listadminpost')
else:
form = NewAdminPostForm()
return render(request, 'back/new-post-blog.html', {'form': form})
#login_required
def listadminpost(request):
posts = Post.objects.all()
return render(request, 'back/list-post-blog.html', {'posts': posts})
Form of my Blog:
from django import forms
from .models import Post, Category
class NewAdminPostForm(forms.ModelForm):
title = forms.CharField(label="Titre de l'article", max_length=255,)
message = forms.CharField(widget=forms.Textarea(),
max_length=4000,
help_text="Contenu de l'article")
pre_message = forms.CharField(label="Message de prévisu",
widget=forms.Textarea(),
max_length=4000,
help_text="Contenu de l'article")
class Meta:
model = Post
fields = ['title','meta_desc','message','pre_message','category']
Model of my Blog:
from django.db import models
from django.contrib.auth.models import User
class Category(models.Model):
name = models.CharField(max_length=255, unique=True)
description = models.TextField(max_length=1000)
def __str__(self):
return self.name
def get_categories_count(self):
return Category.objects.filter(post__category=self).count()
class Post(models.Model):
title = models.CharField(max_length=255)
meta_desc = models.TextField(max_length=320, null=True)
pre_message = models.TextField(max_length=4000, null=True)
message = models.TextField(max_length=4000)
category = models.ForeignKey(Category, on_delete='cascade')
created_at = models.DateTimeField(auto_now_add=True)
created_by = models.ForeignKey(User, on_delete=models.CASCADE,
blank=True, null=True)
def __str__(self):
return self.title
https://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.create
your code:
post.save()...Post.objects.create(
from the link above:
A convenience method for creating an object and saving it all in one step. Thus:
p = Person.objects.create(first_name="Bruce", last_name="Springsteen")
and:
p = Person(first_name="Bruce", last_name="Springsteen")
p.save(force_insert=True)
are equivalent.
So what you do in your code:
you save post object created from form
you create and save another Post instance by calling create method
choose any of them, just one, and this will avoid duplicates.
Instead of doing this:
#login_required
def newadminpost(request):
if request.method == 'POST':
form = NewAdminPostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
Post.objects.create(
message=form.cleaned_data.get('message'),
category_id=post.category_id,
created_by=request.user
)
#post.save()
return redirect('listadminpost')
else:
form = NewAdminPostForm()
return render(request, 'back/new-post-blog.html', {'form': form})
You could do this: cleaner, easier to read, more up-to-date, and easier to maintain:
class AdminCreateView(LoginRequiredMixin, generic.CreateView):
model = Request
form_class = RequestForm
def form_valid(self, form):
result = super(AdminCreateView, self).form_valid(form)
title = form.cleaned_data.get('title')
meta_desc = form.cleaned_data.get('meta_desc')
message = form.cleaned_data.get('message')
# and so on. if there's something you refuse (ex title empty) do this:
if not title:
form.add_error('title', _("Precise the title"))
return self.form_invalid(form)
Post.objects.create(message=message,
meta_desc=meta_desc,
title=title,) # and so on
return result

Updating ModelForm Django 1.10

I'm fairly new to coding in general, so please forgive my ignorance.
I have a ModelForm (Django 1.10 -just in case-) for a 'post' on a social network website:
models.py:
class Post(models.Model):
user = models.ForeignKey(User)
text = models.TextField()
image = models.ImageField(blank=True)
draft = models.BooleanField(default=False)
submission_date = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return self.text
forms.py:
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['text', 'image', 'draft']
To update a post, this is its function:
views.py:
def edit_post(request, post_id):
post = get_object_or_404(Post, pk=post_id)
if not request.user.is_superuser and\
not request.user == post.user:
raise PermissionDenied
if request.method == 'POST':
form = PostForm(request.POST, instance=post)
if form.is_valid():
post = form.save(commit=False)
post.user = request.user
post.save()
context = {'post': post,'form': form}
return HttpResponse(render(request, 'accounts/view_post.html', context))
elif request.method == 'GET':
form = PostForm(request.GET, instance=post)
context = {'post': post,'form': form}
return HttpResponse(render(request, 'accounts/edit_post.html', context))
in the template:
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
{% include 'accounts/form_template.html' %}
<input class="btn btn-btn-success" type="submit" value="Save Post">
</form>
My question is: when trying to update, why does the original field input not show up? The fields turn up empty as if I were creating a new post.
The more detailed answer, the more I would appreciate it.
Thank you in advance.
Turns out all I needed to do was remove 'request.GET':
in views.py:
def edit_post(request, post_id):
post = get_object_or_404(Post, pk=post_id)
...
...
elif request.method == 'GET':
form = PostForm(instance=post)
context = {'post': post,
'form': form}
return HttpResponse(render(request, 'accounts/edit_post.html', context))
Thank you to everyone who tried to help, especially #Zagorodniy Olexiy.
If you want to show original inputs in the form you have to ad some code in your PostForm:
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['text', 'image', 'draft']
def __init__(self, *args, **kwargs):
super(PostForm, self).__init__(*args, **kwargs)
for key in self.fields:
self.fields[key].required = False
then you have to load it in to the view
def edit_post(request, post_id):
post = get_object_or_404(Post, pk=post_id)
if not request.user.is_superuser and\
not request.user == post.user:
raise PermissionDenied
if request.method == 'POST':
form = PostForm(request.POST, request.FILES, instance=post)
if form.is_valid():
post = form.save(commit=False)
post.user = request.user
post.save()
context = {'post': post,
'form': form}
return HttpResponse(render(request, 'accounts/view_post.html', context))
elif request.method == 'GET':
form = PostForm(request.GET, instance=post)
context = {'post': post,
'form': form}
return HttpResponse(render(request, 'accounts/edit_post.html', context))

Categories

Resources