I'm creating a page where my users can edit their articles but there is one problem, my form is not saving the modifications when I submit my form. Let's have a look:
views.py
def edit(request, id=None, template_name='article_edit.html'):
if id:
article = get_object_or_404(Article, id=id)
if article.user != request.user:
return HttpResponseForbidden()
else:
article = Article(user=request.user)
if request.POST:
form = ArticleForm(request.POST, instance=article)
if form.is_valid():
save_it = form.save()
save_it.save()
form.save_m2m()
return HttpResponseRedirect("/")
else:
form = ArticleForm(instance=article)
context = {'form': form}
populateContext(request, context)
return render(request, template_name, context)
line 3 to 8 : Is to check if it's your own articles there is no problem with that.
line 10 to 18 : There is a problem between these lines, my form is not saving when I submit my form
forms.py (ModelForm)
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
exclude = ['date', 'rating', 'user']
form = ArticleForm()
(Line 4 : I'm excluding the date, the ratings and the username when saved so that the user can not change these informations.)
I don't have any problem in my template.html and the urls.py don't bother with that I checked over 10 times :)
Any help would be great, I'm blocked on this problem since over 1 month...
******* EDIT *******
Models.py
class Article(models.Model):
user = models.ForeignKey(User)
titre = models.CharField(max_length=100, unique=True)
summary = RichTextField(null=True, max_length=140)
contenu = RichTextField(null=True)
date = models.DateTimeField(auto_now_add=True, auto_now=False, verbose_name="Date de parution")
image = models.ImageField(upload_to='article', default='article/amazarashi.jpeg')
rating = RatingField(can_change_vote=True)
tags = TaggableManager(through=TaggedItem, blank=True)
def __str__(self):
return self.titre
Template.html
{% block body %}
{% if user.is_authenticated %}
<p>Authentificated as <strong>{{ user.username }}</strong></p>
{% else %}
<p>NOT Authentificated</p>
{% endif %}
<h1>Edit this {{ article.titre }}</h1>
<br>
<div class="col-xs-12 col-sm-8 col-md-8 col-lg-8">
<form enctype='multipart/form-data' action="{% url "article.views.index" %}" method="post" class="form" autocomplete="off" autocorrect="off">
{% csrf_token %}
<div class="form-group">TITRE
{{ form.titre.errors }}
{{ form.titre }}
</div>
<div class="form-group">SUMMARY
{{ form.media }}
{{ form.summary.errors }}
{{ form.summary }}
</div>
<div class="form-group">CONTENU
{{ form.media }}
{{ form.contenu.errors }}
{{ form.contenu }}
</div>
<div class="form-group">
{{ form.image.errors }}
{{ form.image }}
</div>
<div class="form-group">TAGS
{{ form.tags.errors }}
{{ form.tags }}
</div>
<input type="submit" class="btn btn-default" value="Submit"/>
</div>
</form>
{% endblock %}
According to your error message.. Modify your form in templates:
<form enctype='multipart/form-data' ...>
Add request.FILES to your Form creation:
if request.POST:
form = ArticleForm(request.POST, request.FILES, instance=article)
UPDATE:
You have strange url tag parameter. Are you sure you have defined your url like: ('...', your_view, name='article.views.index')? I am in doubt cause url tag takes url name as parameter but not view path and nobody usually uses url names like this.
Related
I need some fresh eyes, what am I missing here? In my Post Model imageField is defined as "picture" to be uploaded on the site, I seed it on my admin panel, it gets uploaded just fine but I can't seem to make it appear on the page: http://127.0.0.1:8000/posts/. I get ValueError at /posts/
The 'picture' attribute has no file associated with it. Highlited line is line 257, in post_comment_create_view
return render(request, 'network/posts.html', context)
Model:
class Post(models.Model):
# id is created automatically by Django
picture = models.ImageField(upload_to='images', blank=True, validators=[FileExtensionValidator(['png', 'jpg', 'jpeg'])])
content = models.TextField()
liked = models.ManyToManyField(Profile, blank=True, related_name="likes")
author = models.ForeignKey(Profile, on_delete=models.CASCADE, null=True)
updated = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('-created',)
def __str__ (self):
return str(self.content[:20])
Forms:
class PostModelForm(forms.ModelForm):
content = forms.CharField(widget=forms.Textarea(attrs={'rows':2}))
class Meta:
model = Post
fields = ('content', 'picture')
Views:
#login_required
def post_comment_create_view(request):
qs = Post.objects.all()
profile = Profile.objects.get(user=request.user)
#Setting up pagination
p = Paginator(qs, 5)
page = request.GET.get('page')
post_list = p.get_page(page)
#Post form, comment form
p_form = PostModelForm()
c_form = CommentModelForm()
post_added = False
profile = Profile.objects.get(user=request.user)
if 'submit_pForm' in request.POST:
print(request.POST)
p_form = PostModelForm(request.POST, request.FILES)
if p_form.is_valid():
instance = p_form.save(commit=False)
instance.author = profile
instance.save()
p_form = PostModelForm()
post_added = True
if 'submit_cForm' in request.POST:
c_form = CommentModelForm(request.POST)
if c_form.is_valid():
instance = c_form.save(commit=False)
instance.user = profile
instance.post = Post.objects.get(id=request.POST.get('post_id'))
instance.save()
c_form = CommentModelForm()
context = {
'qs': qs,
'profile': profile,
'p_form': p_form,
'c_form': c_form,
'post_added': post_added,
'post_list': post_list,
}
return render(request, 'network/posts.html', context)
HTML:
{% block body %}
<div>
<div class="border border-light rounded" style="width: 25rem;">
{% if post_added %}
<div id="alertFade" class="alert alert-success" role="alert">Post added!</div>
{% endif %}
<form action="" method="POST" enctype="multipart/form-data" class="form-group">
{% csrf_token %}
{{p_form}}
<button type="submit" class="btn btn-sm btn-success" name="submit_pForm">Send Post</button>
</form>
</div>
{% for obj in post_list %}
<div class="card center" style="width: 30rem;">
<div class="card-head" style="background-color: #d0e2bc;">
<img width="50px" class="avatar img-thumbnail rounded-circle z-depth-2 ml-1" src={{obj.author.avatar.url}}> {{ obj.author.user }} - {{ obj.created|naturaltime }}
{% if request.user == obj.author.user %}
<button class="btn btn-sm btn-success float-right mt-2 mr-1">Delete</button>
<button class="btn btn-sm btn-success float-right mr-1 mt-2">Edit</button>
{% endif %}
</div>
<div class="card-body">
<img src={{obj.picture.url}}> <br>
<p class="card-text">{{obj.content|safe}}</p>
<hr>
</div>
<button class="cmt_btn ui button mb-5">show / hide comments</button>
<div class="comment-box">
{% if obj.comment_set.all %}
{% for c in obj.comment_set.all %}
<img width="20px" class="avatar img-thumbnail rounded-circle"src={{c.user.avatar.url}} alt="">
<span>{{ c.user }}</span>
<div class="mt-2">{{ c.body }}</div>
{% endfor %}
{% endif %}
There should be quotes around the src:
<img src={{obj.picture.url}}> <!-- will not work -->
Should be:
<img src="{{obj.picture.url}}"> <!-- works -->
This is for the tag attributes. For content within the tag, what you have is fine, no quotes. This is good:
<span>{{ c.user }}</span> <!-- works -->
I solved it, the solution is that I had to check if the img exists:
{% if obj.picture %}
<img src="{{obj.picture.url}}"> <br/>
{% endif %}
I have made a comment system under my books where only the authenticated user can comment. When I use the form to add a comment, it doesn't work! why ?
here is my models
models.py
class Books(models.Model):
author = models.ManyToManyField(Authors)
title = models.CharField(max_length=250)
number_of_pages = models.PositiveIntegerField(validators=[MaxValueValidator(99999999999)])
date_added = models.DateField(auto_now_add=True)
updated = models.DateField(auto_now=True)
publication_date = models.PositiveIntegerField(default=current_year(), validators=[MinValueValidator(300),
max_value_current_year])
cover = models.ImageField(upload_to='pics/covers/', default='pics/default-cover.jpg')
pdf_file = models.FileField(upload_to='pdfs/books/', default='pdfs/default-pdf.pdf')
category = models.ForeignKey(Categories, on_delete=models.CASCADE)
def __str__(self):
return self.title
class Comments(models.Model):
book = models.ForeignKey(Books, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
body = models.TextField()
date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return '{} - {}'.format(self.livre.title, self.user)
here is my forms
forms.py
class BookForm(ModelForm):
class Meta:
model = Books
fields = '__all__'
class CommentForm(ModelForm):
class Meta:
model = Comments
fields = ['body']
here is my views
views.py
#login_required(login_url='login')
def book_detail_view(request, book_id):
books = get_object_or_404(Books, pk=book_id)
context = {'books': books,}
return render(request, 'book_detail.html', context)
#login_required(login_url='login')
def add_comment(request, comment_id):
form = CommentForm()
books = get_object_or_404(Books, pk=comment_id)
user = request.user
if request.method == "POST":
form = CommentForm(request.POST, instance=books)
if form.is_valid():
comment = form.save(commit=False)
comment.user = user
comment.books = books
comment.save()
return redirect('book_detail', books.id)
context = {'form': form}
return render(request, 'comment_form.html', context)
here is my book detail page
book_detail.html
{% extends 'base.html' %}
{% block title %} {{ books.title }} {% endblock %}
{% block content %}
<div class="row">
<div class="col-lg-4">
<p><img src="{{ books.cover.url }}"></p>
</div>
<div class="col-lg-8">
<h2>{{ books.title }}</h2>
<b>Author : </b>
{% for author in books.author.all %}
{{ author.name }}
{% if not forloop.last %},{% endif %}
{% endfor %}<br/>
<b>Catégory : </b>{{ books.category }}<br/>
<b>Pages : </b>{{ books.number_of_pages }}<br/>
<b>Publication : </b>{{ books.publication_date }}<br/>
<b>Date added : </b>{{ books.date_added }}<br/>
<b>Updated : </b>{{ books.updated }}<br/>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<p><button class="btn btn-outline-dark btn-sm"><i class="far fa-eye"></i> Read</button></p>
</div>
</div>
<hr/>
<div class="container-fluid">
<h2>Comments</h2>
</div>
<div class="container-fluid">
{% if not books.comments.all %}
<p>No comments yet ! <a class="text-primary" href="{% url 'add_comment' books.id %}">Add comment...</a></p>
{% else %}
<a class="text-primary" href="{% url 'add_comment' books.id %}">Add comment !</a><br/><br/>
{% for comment in books.comments.all%}
<b>{{ comment.user }}</b> - <span class="text-muted" style="font-size: 13px;">{{ comment.date }}</span>
<p>{{ comment.body }}</p>
{% endfor %}
{% endif %}
</div>
{% endblock %}
here is my form for comment model
comment_form.html
{% extends 'base.html' %}
{% block title %} Add a comment {% endblock %}
{% block content %}
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Add this comment</button>
</form>
{% endblock %}
here is my urls
urls.py
urlpatterns = [
# BOOKS
path('book/<int:book_id>/', views.book_detail_view, name='book_detail'),
# COMMENTS
path('book/<int:comment_id>/comment/', views.add_comment, name='add_comment'),
]
Your form is currently set to edit the book, not the comment, you should remove the instance=books:
if request.method == "POST":
# no instance=books ↓
form = CommentForm(request.POST)
If you use instance=books, the form will set attributes to the Books object, and then the comment = form.save(commit=False) will result in the fact that comment is a Books object that is already saved and thus updated in the database.
You also made a typo when setting the book of a Comments object: it is book, not books:
if form.is_valid():
comment = form.save(commit=False)
comment.user = user
comment.book = books # ← .book, not .books
comment.save()
Note: normally a Django model is given a singular name, so Book instead of Books.
it saves and displays comments on the options.html page if the user is already logged in but when the user logs in after comment section redirects to the login page it displays the value error as this:
NoReverseMatch at /rank/best-trekking-destination-in-nepal/comment/
Reverse for 'comment' with arguments '('',)' not found. 1 pattern(s) tried: ['rank/(?P[^/]+)/comment/$']
Request Method: GET
Request URL: http://127.0.0.1:8000/rank/best-trekking-destination-in-nepal/comment/
Django Version: 2.1.5
Exception Type: NoReverseMatch
Exception Value:
Reverse for 'comment' with arguments '('',)' not found. 1 pattern(s) tried: ['rank/(?P[^/]+)/comment/$']
urls.py
path('<slug>/',views.options,name='options'),
path('<slug>/comment/',views.comment,name='comment'),
views.py
def options(request,slug):
category = Category.objects.get(slug=slug)
category.views += 1
category.save()
options = category.option_set.all().order_by('-votes')
try:
for option in options:
option.has_voted = option.vote_set.filter(voter=request.user).exists()
except:
options = category.option_set.all().order_by('-votes')
form = CommentForm()
return render(request, 'rank/options.html', {'options': options,'form':form,'title': 'options','category':category})
#login_required(redirect_field_name='next',login_url='rank:login')
def comment(request,slug):
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.category = Category.objects.get(slug=slug)
comment.user = request.user
comment.save()
messages.success(request, 'Comment Posted.')
return redirect('rank:options', slug)
else:
form = CommentForm()
return render(request, 'rank/options.html', {'form': form})
def my_login(request):
if request.method == "POST":
form = LoginForm(request.POST)
username = form['username'].value()
password = form['password'].value()
user = authenticate(username=username,password=password)
if user:
login(request,user)
redirect_url = request.GET.get('next','rank:home')
return redirect(redirect_url)
else:
messages.error(request,'Invaid Username or Password')
else:
form = LoginForm()
return render(request,'rank/login.html',{'form':form})
options.html
{% extends "rank/base.html" %}
<title>{% block title %}{{title}}{% endblock title%}</title>
{% load bootstrap4 %}
{% block content %}
<center><br>
<ol type="1">
<center>{% bootstrap_messages %}</center>
{% for option in options %}
<div class="col-lg-6 col-md-6 mb-6">
<div class="card h-100">
<div class="card-body">
<b><li>
<img src="/media/{{option.image}}" width="400" height="300">
<h4>{{option.name}}
</h4>
<h5 class="card-text">{{ option.details}}</h5>
<h5>{{ option.votes }} votes</h5>
{% if option.has_voted %}
<p class="btn btn-success">Voted</p>
{% else %}
<form action="{% url 'rank:vote' option.slug %}" method="post">
{% csrf_token %}
<input type="submit" class="btn btn-success" value="Vote" >
</form>
{% endif %}
</li></b>
</div>
<div class="card-footer">
<small class="text-muted"></small>
</div>
</div>
</div>
{% empty %}
<div class="card w-100">
<div class="card-body">
<h4>Item not available</h4>
</div>
</div>
{% endfor %}
</ol>
<h3>{{ category.comment_set.all|length}} comments</h3>
<hr>
{% for c in category.comment_set.all %}
<div class="col-lg-6 col-md-6 mb-6">
<div class="card-footer text-muted">
<b>{{ c.user.username}} </b>  {{c.created|timesince}} ago
</div>
<div class="card-body">
<p class="card-text">{{ c.comment}}</p>
</div>
</div>
{% endfor %}
<hr>
<div class="col-lg-6 col-md-6 mb-6">
<form method="post" action="{% url 'rank:comment' category.slug %}">
{% csrf_token %}
{% bootstrap_form form %}
<input type="submit" class="btn btn-success" value="Post Comment">
</form>
</div>
</center>
{% endblock content%}
you comment function in views.py is incomplete. you don't handle GET and other types of request in there:
#login_required(redirect_field_name='next',login_url='rank:login')
def comment(request,slug):
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.category = Category.objects.get(slug=slug)
comment.user = request.user
comment.save()
messages.success(request, 'Comment Posted.')
return redirect('rank:options',slug)
else:
form = CommentForm()
return render(request,'...some_template',{'form':form})
UPDATE: your next error NoReverseMatch is occured because vairable category (and also options) not send from views to the options.html template and therefor it is null in the template (arguments '('',)' not found). you can fix that like this:
#login_required(redirect_field_name='next',login_url='rank:login')
def comment(request,slug):
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.category = Category.objects.get(slug=slug)
comment.user = request.user
comment.save()
messages.success(request, 'Comment Posted.')
return redirect('rank:options', slug)
else:
form = CommentForm()
category = Category.objects.get(slug=slug)
options = category.option_set.all().order_by('-votes')
return render(request, 'rank/options.html', {'options': options,'form':form,'title': 'options','category':category})
#login_required(redirect_field_name='next',login_url='rank:login')
def comment(request,slug):
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.category = Category.objects.get(slug=slug)
comment.user = request.user
comment.save()
messages.success(request, 'Comment Posted.')
return redirect('rank:options',slug)
return redirect('some default view')
You need to add a view to each branch of computation if your POST request view fails or the form is not valid then a view is not returned. I put a line of code you need to finish off.
I'm trying to implement both searching posts and create a post in single view.
views.py
class HomeView(TemplateView):
template_name = 'home/home.html'
def get(self, request):
post_list = None
form = HomeForm(self.request.GET or None)
form1 = CommentForm(self.request.GET or None)
posts = Post.objects.filter(user = request.user).order_by('-created')
comments = Comment.objects.all()
users = User.objects.exclude(id=request.user.id)
query = request.GET.get('q')
if query:
post_list = Post.objects.filter(
Q(post__icontains=query)
)
context = {
'posts_list': post_list, }
print(post_list)
args = {
'form': form, 'posts': posts, 'users': users, 'form1':form1,
'comments':comments,'post_list':post_list,
}
return render(request, self.template_name, args)
def post(self, request):
form1 = CommentForm()
text = ''
if request.method=='GET' and 'btn1' in request.POST:
post_list = Post.published.all()
query = request.GET.get('q')
if query:
post_list = Post.objects.filter(
Q(post__icontains=query)
)
context = {
'posts_list': posts_list,
}
return redirect('home:home')
if request.method=='POST' and 'btn' in request.POST:
form = HomeForm(request.POST,request.FILES)
if form.is_valid():
post = form.save(commit=False)
post.user = request.user
post.save()
text = form.cleaned_data['post']
form = HomeForm()
form1 = CommentForm()
return redirect('home:home')
html code
<form enctype="multipart/form-data" class="form-inline my-2 my-lg-0" action=".">
<input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search" name='q'>
<button class="btn btn-outline-success my-2 my-sm-0" name="btn1" type="submit">Search</button>
{% block body %}
<div class="container">
<div class="col-md-8">
<h2>Home</h2>
<form method="POST" enctype="multipart/form-data" type = 'file'>
{% csrf_token %}
{{ form.post }}
{{ form.image }}
<br>
<button name="btn" type="submit">Submit</button>
</form>
<h2>{{ text }}</h2>
{% for post in posts %}
<h1>{{post.id}}.{{ post.post }}</h1>
<br>
<img src="{{ post.image.url }}" width = 240 >
<p>Posted by {{ post.user.get_full_name }} on {{ post.created }}</p>
<!-- <form action="{% url 'home:cmnt' pk=post.id %}" method="POST" enctype="multipart/form-data" type = 'file'>
{% csrf_token %}
{{ form1.content }}
<br>
<button type="submit">Submit</button>
</form> -->
{{comments.count}} comment{{comments|pluralize}}<br>
{% for comment in post.comment_set.all %}
<small><b>{{ comment.comment }}</b></small>
<p>commented by {{ comment.user.get_full_name }} on {{ comment.created }}</p>
{% endfor %}
{% endfor %}
</div>
</div>
{% endblock %}
from this code I get the query value entered by the user.
when I implement this code I need to fill both the forms post create and search form but, I need to submit only one form at a time
when I fill both the forms and submit I get URL like this
http://127.0.0.1:8000/home/?q=django+&csrfmiddlewaretoken=OsNRlkvRjSHHuNYMxhZS9MFushCzCTZtjFvgEb5qFFybovBhWI3X0uufvd8bO8WS&post=asdsad&image=7653_1792089421024273_590924154993413545_n.jpg&btn=
I need a code which implements:
when submitted post create form create the post and return
when submitted search form load the respective results from database
I've tried looking around for solutions on how to check if a form's name is already existing in the database. I used this link to figure out how, and it is indeed not allowing duplicate names to be entered. But where I expected one, I did not get an error message. I'm not sure what I'm doing wrong here, so if anyone can tell me what I should do, that would be really useful!
addgame.html:
<form method="POST" class="post-form" enctype="multipart/form-data">
{% csrf_token %}
{% if form.non_field_errors %}
{% for error in form.non_field_errors %}
{{ error }}
{% endfor %}
{% endif %}
<div class="form-group">
{{ form.name.label_tag }}
{% render_field form.name class="form-control" %}
<br>
{{ form.genre.label_tag }}
{% render_field form.genre class="form-control" %}
<br>
{{ form.image.label_tag }}
{{ form.image }}
</div>
<hr>
<button type="submit" class="save btn btn-primary">Save</button>
</form>
views.py:
def addgame(request):
if request.method == "POST":
form = InfoForm(request.POST, request.FILES)
if form.is_valid():
infolist = form.save(commit=False)
infolist.created_date = timezone.now()
infolist.save()
return redirect('index')
else:
form = InfoForm()
return render(request, 'ranking/addgame.html', {'form': form})
forms.py:
class InfoForm(forms.ModelForm):
class Meta:
model = GameInfo
fields = ('name', 'image', 'genre')
def clean_name(self):
name = self.cleaned_data['name']
try:
match = GameInfo.objects.get(name=name)
except GameInfo.DoesNotExist:
return name
raise forms.ValidationError('This game has already been added to the list.')
not sure if needed, so I'll post models.py as well:
class GameInfo(models.Model):
GAME_CHOICE = [
("BMU", "Beat 'em up"),
("FT", "Fighting"),
("PF", "Platform"),
("FPS", "Shooter"),
("SV", "Survival"),
("ST", "Stealth"),
("AA", "Action Adventure"),
("EX", "Exploring"),
("SH", "Survival horror"),
("IF", "Interactive fiction"),
("IM", "Interactive movie"),
("VN", "Visual novel"),
("ARP", "Action role-playing"),
("JRP", "Japanese role-playing"),
("TRP", "Tactical role-playing"),
("CAM", "Construction and management"),
("LS", "Life simulation"),
("SP", "Sports"),
("VH", "Vehicle"),
("MOBA", "Multiplayer online battle arena"),
("RTS", "Real-time strategy"),
("RTT", "Real-time tactics"),
("TBS", "Turn-based strategy"),
("TBT", "Turn-based tactics"),
("MMORPG", "MMORPG"),
("MMOFPS", "MMO-FPS"),
("MMOR", "MMO Racing"),
("CG", "Cardgame"),
("PAC", "Point and Click"),
("MG", "Music Game"),
("VR", "Virtual Reality"),
("RC", "Racing"),
]
name = models.CharField(max_length=100)
created_date = models.DateTimeField(default=timezone.now)
image = models.ImageField(upload_to='./media/images/')
genre = models.CharField(
max_length=6,
choices=GAME_CHOICE,
default="BMU",
)
def __str__(self):
return self.name
class Meta:
ordering = ('name',)
If you are rendering the form fields manually, then it's up to you to include the errors, for example:
{{ form.name.errors }}
{{ form.name.label_tag }}
{% render_field form.name class="form-control" %}