I've created a new form for comments the articles on a website. When I add the new comment from django admin everything works ok, but when I try to add the new comment directly from detail page nothings happen and I'am redirecting to the page with list of articles.
here are my files
models.py:
class Komentarz(models.Model):
post = models.ForeignKey(Wpisy, related_name="komentarze", verbose_name="Komentarze do artykułu", on_delete=models.CASCADE)
name = models.CharField(max_length=80, verbose_name="Imię")
email = models.EmailField(verbose_name="Email")
content = models.TextField(verbose_name="Treść komentarza")
created_date = models.DateTimeField(verbose_name="Utworzono", auto_now_add=True)
active = models.BooleanField(verbose_name="Aktywny?", default=True)
class Meta:
ordering = ('created_date',)
verbose_name="Komentarz"
verbose_name_plural="Komentarze"
def __str__(self):
return 'Komentarz dodany przez {} dla strony {}'.format(self.name, self.post)
vies.py with the function of details
from django.shortcuts import render, get_object_or_404
from .models import Wpisy, Komentarz
from .forms import KomentarzeForma
....
def detale_bajki(request, slug, ):
detale_bajki = get_object_or_404(Wpisy, slug=slug)
komentarze = detale_bajki.komentarze.filter(active=True)
if request.method == 'POST':
formularz_komentarza = KomentarzeForma(data=request.POST)
if formularz_komentarza.is_valid():
nowy_komentarz = formularz_komentarza.save(commit=False)
nowy_komentarz.detale_bajki = detale_bajki
nowy_komentarz.save()
else:
formularz_komentarza = KomentarzeForma()
return render(request, 'bajki/detale_bajki.html', {'detale_bajki': detale_bajki, 'komentarze': komentarze, 'formularz_komentarza': formularz_komentarza})
forms.py
from .models import Komentarz
from django import forms
class KomentarzeForma(forms.ModelForm):
class Meta:
model = Komentarz
fields = ('name', 'email', 'content')
and detail.html
...
{% with komentarze.count as total_komentarze %}
<h2>
{{ total_komentarze }} komentarz{{ total_komentarze|pluralize:"y" }}
</h2>
{% endwith %}
{% for komentarz in komentarze %}
Komentarz dodany przez <strong>{{komentarz.name}}</strong>
{{komentarz.created_date}}
<p>
{{ komentarz.content|linebreaks }}<br>
{% empty %}
<p>Nie dodano jeszcze żadnych komentarzy</p>
{% endfor %}
{% if nowy_komentarz %}
<h2>Twój komentarz został dodany</h2>
{% else %}
<h2>Dodaj nowy komentarz</h2>
<form action="." method="post">
{{formularz_komentarza.as_p}}
{% csrf_token %}
<p><input type="submit" value="Dodaj komentarz"></p>
</form>
{% endif %}
clas Wpisy in models.py
class Wpisy(models.Model):
title = models.CharField(max_length=400, verbose_name="Tytuł")
slug = models.SlugField(unique=True, max_length=400,verbose_name="Przyjazny adres url")
content = models.TextField()
status_audio = models.BooleanField(default=False, verbose_name="Czy dostępny będzie plik audio?")
audio_file = models.FileField(upload_to='uploads/',verbose_name="Plik audio")
created_date = models.DateTimeField(blank=True, null=True, verbose_name="Data utworzenia")
category = models.ForeignKey(Kategorie, verbose_name="Kategoria", on_delete=models.CASCADE)
class Meta:
verbose_name="Wpis"
verbose_name_plural="Wpisy"
def __str__(self):
return self.title
Your url pattern is
path('bajki/<slug>', views.detale_bajki, name='detale_bajki')
Note that it doesn't have a trailing slash. Your form's action is "."
<form action="." method="post">
That means you are submitting to /bajki/, which is the wrong URL.
You could fix this by adding a trailing slash to the url (which is common in Django URLs)
path('bajki/<slug>/', views.detale_bajki, name='detale_bajki')
Or you could change the form action to "" instead of ".". In the comments it looks like you fixed the issue by changing the form action to {{ detale_bajki.slug }}.
However these changes to the form action are fragile, and could break if you change your URL patterns again. The best approach is to use the {% url %} tag to reverse the correct URL.
<form action="{% url 'detale_bajki' detale_bajki.slug %}" method="post">
Try out:
nowy_komentarz.post = detale_bajki
...
return render(request, 'html page', {'key': 'what you want to return to your context'}) # don't fornget to add some return to your view.
Related
I'd like to make a POST REQUEST with an existing HTML FORM, to store it in a database, but everytime I tried to submit data through the form, it gives me the following error
By the way I'm doing this following those post:
Django form using HTML template
MultiValueDictKeyError generated in Django after POST request on login page
Registro.html
{% extends "RegistrarProyecto/layout.html" %}
{% block title %} {% endblock %}
{% block content %}
<h1>Crear Usuario</h1>
<form class="CrearUsuario" action="{% url 'registro' %}" method="POST">
{% csrf_token %}
<input type="text",name="NombreProyecto", placeholder="Nombre del Proyecto" >
<br>
<br>
<input type ="text", name="ResponsableProyecto", placeholder ="Responsable del proyecto">
<br>
<br>
<textarea name="DescripcionProyecto", rows="4", cols="50"></textarea>
<br>
<br>
<input type="submit" value="Registrar">
</form>
<br>
Ir a Bienvenido
{% endblock %}
views.py
def registro(request):
if request.method == 'POST':
NombreProyecto = request.POST.get('NombreProyecto')
ResponsableProyecto = request.POST.get('ResponsableProyecto')
DescripcionProyecto = request.POST.get('DescripcionProyecto')
Proyecto.objects.create(NombreProyecto=NombreProyecto,ResponsableProyecto=ResponsableProyecto,DescripcionProyecto=DescripcionProyecto)
return redirect("RegistrarProyecto/index.html")
return render(request, 'RegistrarProyecto/Registro.html')
models.py
from django.db import models
# Create your models here.
class Proyecto(models.Model):
NombreProyecto = models.CharField(max_length = 64)
ResponsableProyecto = models.CharField(max_length= 64)
DescripcionProyecto = models.CharField(max_length = 64)
def __str__(self):
return f"{self.NombreProyecto}"
class Evaluador(models.Model):
NombreEvaluador = models.CharField(max_length=64)
Proyecto = models.ManyToManyField(Proyecto, blank=True, related_name="Evaluador")
def __str__(self):
return f"{self.NombreEvaluador} {self.Proyecto}"
urls.py
from django.urls import path
from . import views
urlpatterns = [
path("", views.index, name="index"),
path("Registro", views.registro, name="registro")
]
it is this line:
<input type="text",name="NombreProyecto", placeholder="Nombre del Proyecto" >
change it to:
<input type="text", name="NombreProyecto", placeholder="Nombre del Proyecto" >
I'd suggest two things. 1. Either you set your model field as null-able that even it doesn't get a value for one of those fields it will still be saved. For example:
class Proyecto(models.Model):
NombreProyecto = models.CharField(max_length=64, null=True, blank=True)
ResponsableProyecto = models.CharField(max_length=64, null=True, blank=True)
DescripcionProyecto = models.CharField(max_length=64, null=True, blank=True)
def __str__(self):
return f"{self.NombreProyecto}"
Set your html input and textarea fields with the required attribute required. That values are passed for all fields.
I am trying to add a comment section to add a comment section to my blog detail using django but when i run my server i get no error in the development server and the comments are not being displayed. I added the comments from my admin site.
The snippet of my code is below.
views.py
from .models import Post
from django.utils import timezone
from .forms import PostForm, CommentsForm
from django.contrib.auth.decorators import user_passes_test
# Create your views here.
def home(request):
return render (request, 'blogapp/home.html')
def blog_list(request):
post = Post.objects.order_by('-published_date')
context = {
'posts':post
}
return render(request, 'blogapp/blog_list.html', context)
def blog_detail(request, pk=None):
detail = Post.objects.get(pk=pk)
context = {
'detail': detail
}
return render(request, 'blogapp/blog_detail.html', context)
def add_post(request, pk=None):
if request.method == "POST":
form = PostForm(request.POST)
if form.is_valid:
body = form.save(commit=False)
body.published_date = timezone.now()
body.save()
return redirect('blog_list')
form = PostForm()
else:
form = PostForm()
context = {
'form': form
}
return render(request, 'blogapp/add_post.html', context)
def add_comments(request, pk=None):
if request.method == "POST":
form = CommentsForm(request.POST)
if form.is_valid:
comment = form.save(commit=False)
comment.date_added = timezone.now()
comment.save()
return redirect('blog_detail')
form = CommentsForm()
else:
form = CommentsForm()
context = {
'form': form
}
return render(request, 'blogapp/add_comments.html', context)
urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.home, name="homepage"),
path('blog/', views.blog_list, name="blog_list"),
path('blog/post/<int:pk>/', views.blog_detail, name="blog_detail"),
path('blog/add_post/', views.add_post, name="add_post"),
path('blog/add_comments/', views.add_comments, name="add_comments"),
]
forms.py
from django import forms
from .models import Post, Comments
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ('author', 'title', 'post_description', 'image', 'image_description', 'body',)
class CommentsForm(forms.ModelForm):
class Meta:
model = Comments
fields = ('post', 'name', 'body',)
models.py
from django.db import models
from django.utils import timezone
# Create your models here.
class Post(models.Model):
author = models.ForeignKey('auth.user', on_delete=models.CASCADE)
title = models.CharField(max_length=300)
body = models.TextField()
post_description = models.CharField(max_length=500, blank=True, null=True)
image = models.ImageField(blank=True, null=True, upload_to="image/")
image_description = models.CharField(max_length=500, blank=True, null=True)
published_date = models.DateTimeField(default=timezone.now, blank=True, null=True)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.title
class Comments(models.Model):
post = models.ForeignKey('Post', related_name="comments", on_delete=models.CASCADE)
body = models.TextField()
name = models.CharField(max_length=300)
date_added = models.DateTimeField(default=timezone.now, blank=True, null=True)
def __str__(self):
return '%s - %s' % (self.post.title, self.name)
blog_detail.html
{% extends 'base.html' %}
{% load static %}
{% block content %}
<article>
<strong>
<h1><b>{{ detail.title }}</b></h1>
</strong>
<h3>POST AUTHOR: {{ detail.author }}</h3>
<h4><i>{{ detail.post_description }}</i></h4>
<h4>PUBLISHED:{{ detail.published_date }}</h4>
<p>
<hr>
{% if detail.image %}
<center>
<br>
<img src="{{ detail.image.url }}" width="1000" height="700">
<br><br>
<i>IMAGE DESCRIPTION: {{ detail.image_description }}</i>
</center>
{% endif %}
<hr>
<br><br><br>
{{ detail.body|linebreaksbr }}
</p>
<hr class="solid">
<h2>COMMENTS ...</h2>Add One
{% for comment in post.comments.all %}
<strong>
{{ comment.name }}-{{ comment.date_added }}
</strong>
{{ comment.body }}
{% endfor %}
</article>
{% endblock %}
add_comments.html
{% extends 'base.html' %}
{% block content %}
<article>
{% if user.is_authenticated %}
<h1>CREATE NEW BLOG POST.</h1>
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">ADD COMMENT</button>
<input type="hidden" name="next" value="{% url 'blog_detail' pk=post.pk %}"/>
</form>
{% else %}
<h2>Login HERE to add comments.</h2>
{% endif %}
</article>
{% endblock %}
in your template you use
{% for comment in post.comments.all %}
but in template context there is no post variable
you should use {% for comment in detail.comments.all %}
I'm working on a school project. Right now any user can ask a question.
In order to notify all the users when any users asks a question I've created a new app & notifying them through the simple 'view' whenever a question is asked. But it's just plain notifications yet.
How can I mark them read once a user opens the Notification tab? Just like on social networks!
I suggest you to use ContentType to make a dynamic notifications fo any models. This snippet below is an example how to implement the notification system;
1. in your models.py
from django.db import models
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey
from django.utils.translation import ugettext_lazy as _
class ContentTypeToGetModel(object):
"""
requires fields:
- content_type: FK(ContentType)
- object_id: PositiveIntegerField()
"""
def get_related_object(self):
"""
return the related object of content_type.
eg: <Question: Holisticly grow synergistic best practices>
"""
# This should return an error: MultipleObjectsReturned
# return self.content_type.get_object_for_this_type()
# So, i handle it with this one:
model_class = self.content_type.model_class()
return model_class.objects.get(id=self.object_id)
#property
def _model_name(self):
"""
return lowercase of model name.
eg: `question`, `answer`
"""
return self.get_related_object()._meta.model_name
class Notification(models.Model, ContentTypeToGetModel):
# sender = models.ForeignKey(
# User, related_name='notification_sender')
receiver = models.ForeignKey(
User, related_name='notification_receiver')
content_type = models.ForeignKey(
ContentType, related_name='notifications', on_delete=models.CASCADE)
object_id = models.PositiveIntegerField(_('Object id'))
content_object = GenericForeignKey('content_type', 'object_id')
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
STATUS_CHOICES = (
('reply', _('a reply')),
('comment', _('a comment')),
('message', _('a message'))
)
status = models.CharField(
_('Status'), max_length=20,
choices=STATUS_CHOICES, default='comment')
is_read = models.BooleanField(
_('Is read?'), default=False)
def __str__(self):
title = _('%(receiver)s have a %(status)s in the %(model)s:%(id)s')
return title % {'receiver': self.receiver.username, 'status': self.status,
'model': self._model_name, 'id': self.object_id}
class Meta:
verbose_name_plural = _('notifications')
ordering = ['-created']
2. in your views.py
from django.views.generic import (ListView, DetailView)
from yourapp.models import Notification
class NotificationListView(ListView):
model = Notification
context_object_name = 'notifications'
paginate_by = 10
template_name = 'yourapp/notifications.html'
def get_queryset(self):
notifications = self.model.objects.filter(receiver=self.request.user)
# mark as reads if `user` is visit on this page.
notifications.update(is_read=True)
return notifications
3. in your yourapp/notifications.html
{% extends "base.html" %}
{% for notif in notifications %}
{{ notif }}
{# for specific is like below #}
{# `specific_model_name` eg: `comment`, `message`, `post` #}
{% if notif._model_name == 'specific_model_name' %}
{# do_stuff #}
{% endif %}
{% endfor %}
So, when I creat that notifications?
eg: when the other user send a comment to receiver on this post.
from django.contrib.contenttypes.models import ContentType
def send_a_comment(request):
if request.method == 'POST':
form = SendCommentForm(request.POST)
if form.is_valid():
instance = form.save(commit=False)
#instance.sender = request.user
...
instance.save()
receiver = User.objects.filter(email=instance.email).first()
content_type = ContentType.objects.get(model='comment')
notif = Notification.objects.create(
receiver=receiver,
#sender=request.user,
content_type=content_type,
object_id=instance.id,
status='comment'
)
notif.save()
How about menu? Like this stackoverflow, facebook, instagram, or else?
you can handle it with templatetags, eg:
# yourapp/templatetags/notification_tags.py
from django import template
from yourapp.models import Notification
register = template.Library()
#register.filter
def has_unread_notif(user):
notifications = Notification.objects.filter(receiver=user, is_read=False)
if notifications.exists():
return True
return False
and the navs.html menu:
{% load notification_tags %}
{% if request.user.is_authenticated %}
<ul class="authenticated-menu">
<li>
<a href="/notifications/">
{% if request.user|has_unread_notif %}
<i class="globe red active icon"></i>
{% else %}
<i class="globe icon"></i>
{% endif %}
</a>
</li>
</ul>
{% endif %}
First you need ManyToManyField for user module who is readed a post.
profile.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
subscribed = models.ManyToManyField(User, related_name='subscribed', blank=True)
readed = models.ManyToManyField("blogs.BlogPost", related_name='readed', blank=True)
updated = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.user.username)
class Meta:
ordering = ("-created",)
If you have a blog model like this:
class BlogPost(models.Model):
author = models.ForeignKey(Profile, on_delete=models.CASCADE)
post_title = models.CharField("Post Title", max_length=150, unique=True)
post_content = models.TextField("Content")
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.post_title
class Meta:
ordering = ("-created",)
You need to create a button function in blog posts view.
def mark_as_read_button(request):
if request.method == "POST":
my_profile = Profile.objects.get(user=request.user)
post = request.POST.get("post_pk")
obj = BlogPost.objects.get(pk=post)
if obj in my_profile.readed.all():
my_profile.readed.remove(obj)
else:
my_profile.readed.add(obj)
return redirect(request.META.get("HTTP_REFERER"))
return redirect("blogs:subscribed-blogs")
Then you can use is in the template file like this:
{% extends 'base.html' %}
{% block title %}Subscribed Blog Posts{% endblock %}
{% block content %}
{% for post in posts %}
<h5 class="card-title">{{ post.post_title }}</h5>
{% if post in readed %}
<form action="{% url "blogs:mark_as_read" %}" method="POST">
{% csrf_token %}
<input type="hidden" name="post_pk" value={{ post.pk }}>
<button type="submit" class="btn btn-danger btn-sm">Mark as unread</button>
</form>
{% else %}
<form action="{% url "blogs:mark_as_read" %}" method="POST">
{% csrf_token %}
<input type="hidden" name="post_pk" value={{ post.pk }}>
<button type="submit" class="btn btn-success btn-sm">Mark as read</button>
</form>
{% endif %}
<p class="card-text">{{ post.created }}</p>
<p class="card-body">{{ post.post_content }}</p>
<hr>
{% endfor %}
{% endblock %}
Good coding.
The Django project that I am working on lists patient details and lets the user edit the details. I have been able to list it out but views.py is not getting linked to the url for updating the list.
views.py:
def update_patient(request, patient_id):
patient = Patient.objects.get(id=patient_id)
if request.method != 'POST':
form = PatientForm(instance=patient)
else:
# POST data submitted; process data.
form = PatientForm(instance=patient, data=request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('patient:patient',
args=[patient.id]))
context = { 'patient': patient, 'form': form}
return render(request, 'patient/update_patient.html', context)
models.py:
class Patient(models.Model):
patientID = models.CharField(max_length=20)
firstName =models.CharField(max_length=20)
lastName = models.CharField(max_length=20)
age = models.IntegerField(max_length=None)
SSN = models.CharField(max_length=15)
address = models.CharField(max_length=200)
date_added = models.DateTimeField(auto_now_add=True)
urls.py:
url(r'^patients/(?P<patient_id>\update\d+)/$', views.update_patient, name='update'),
update_patient.html:
{% extends "patient/base.html" %} {% block content %}
<p>{{ patient }}
</p>
<p>Update Patient:</p>
<form action="{% url 'patient:update' patient.id %}" method='post'>
{% csrf_token %} {{ form.as_p }}
<button name='submit'>add entry</button>
</form>
{% endblock content %}
Your URL pattern is wrong, you have \update within capturing group, it shouldn't be, change the pattern to this:
url(r'^patients/(?P<patient_id>\d+)/update/$', views.update_patient, name='update')
I need to create a comment form on my site. When I try to display comment_form in HTML instead of the browser, I receive this error:
[u'ManagementForm data is missing or has been tampered with'].
Why is this? How can I fix it?
Here is models.py:
class Category(models.Model):
category_name = models.CharField(max_length=255)
def __unicode__(self):
return u'{0}'.format(self.category_name)
class Category_Form(forms.Form):
category_name = forms.CharField(label='Category_name', max_length=255)
class Comments(models.Model):
comment = models.CharField(max_length=512)
article = models.ForeignKey('Article')
class Comments_Form(forms.Form):
comment = forms.CharField(label='Comment', max_length=512)
article_id = forms.CharField()
class Article(models.Model):
author = models.CharField(max_length=100)
title = models.CharField(max_length=255)
short_text = models.CharField(max_length=1024)
full_text = models.CharField(max_length=5024)
date = models.DateTimeField(default=datetime.now())
category = models.ForeignKey('Category')
class Article_Form(forms.Form):
author = forms.CharField(label='author_article', max_length=100)
title = forms.CharField(label='title_article', max_length=255)
short_text = forms.CharField(label='short_text_article', max_length=1024, widget=CKEditorWidget())
full_text = forms.CharField(label='full_text_article', max_length=5024, widget=CKEditorWidget())
date = forms.DateTimeField()
category = forms.ModelChoiceField(queryset=Category.objects.order_by('category_name'))
My views.py:
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render
from django import forms
from django.template import RequestContext
from sghome.models import News_Form, News, Article_Form, Article, Category, Category_Form, User, User_Form, \
Comments_Form, Comments
from django.forms.formsets import formset_factory
from django.shortcuts import render_to_response
from django.core.urlresolvers import reverse
def Show_Article_Site(request, num):
article = list(Article.objects.filter(id=num))
CommentFormSet = formset_factory(Comments_Form)
formset = CommentFormSet(request.POST, request.FILES)
# formset = CommentFormSet()
return render_to_response('Show_Article_Site.html', {'article': article, 'formset': formset, },
context_instance=RequestContext(request))
def Create_Comment(request):
if request.method == 'POST':
# formset = CommentFormSet(request.POST, request.FILES)
Comment = Comments()
Comment.comment = request.POST['form-0-comment']
else:
Show_Article_Site.formset = Show_Article_Site.CommentFormSet()
return HttpResponseRedirect(reverse(Show_Article_Site))
And my template:
{% extends "base_site.html" %}
{% block center_site %}
{% csrf_token %}
<div class="create_news">
{% for artic in article %}
<br><p>Автор: </p> {{ artic.author }}
<br><p>Дата: </p> {{ artic.date }}
<br><p>Заголовок: </p>{{ artic.title }}
<br><p>Текст: </p>{{ artic.full_text }}
{% endfor %}
<form method="post" action="/Create_Comment/">
{% for form in formset %}
{{ form.comment }}
<input type="submit" value="Add">
{% endfor%}
</form>
</div>
{% endblock %}
Use
formset = CommentFormSet()
Instead of
formset = CommentFormSet(request.POST, request.FILES)
Cause your request.POST and request.FILES are not initiated in that place