I am working on a project that allows student to apply to borrow book from a library and then the librarian decides to approve or decline the request.
models.py
class PendingRequest(models.Model):
book_request = models.ForeignKey(Borrow, on_delete=models.CASCADE, null=True)
member = models.ForeignKey(User, on_delete=models.CASCADE, default=None, null=True)
book = models.ForeignKey(Books, on_delete=models.CASCADE, default=None, null=True)
approved = models.BooleanField(default=False)
declined = models.BooleanField(default=False)
approval_date = models.DateTimeField(auto_now=True, null=True)
I am using a modelformset_factory to render out the form to update multiple instance of the above model. What i want to achieve is to be able to set declined field to True and approved to False whenever the librarian doesn't approve the request
views.py
def approve(request, pk):
member = get_object_or_404(User, id=pk)
pending_requests = PendingRequest.objects.filter(member=member)
PendingRequestFormSet = modelformset_factory(
PendingRequest, form=ApproveForm, extra=0
)
if request.method == "POST":
formset = PendingRequestFormSet(request.POST, queryset=pending_requests)
approved = request.POST.get(formset.prefix + "-approved")
if formset.is_valid():
instances = formset.save(commit=False)
for instance in instances:
instance.book_request = instance.book_request
instance.member = instance.member
instance.book = instance.book
instance.save()
else:
formset = PendingRequestFormSet(queryset=pending_requests)
context = {"formset": formset, "member": member}
return render(request, "books/approve.html", context)
form.py
class ApproveForm(forms.ModelForm):
approved = forms.BooleanField(required=False)
class Meta:
model = PendingRequest
fields = [
"approved",
"book",
]
template
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Approve Request</title>
</head>
<body>
<h1>Update Pending Requests for {{ member.username }}</h1>
<form method="post">
{% csrf_token %}
{{ formset.management_form }}
<table>
<thead>
<tr>
<th>Book</th>
<th>Approved</th>
</tr>
</thead>
<tbody>
{% for form in formset %}
<tr>
<td>{{form.book}}</td>
<td>{{form.approved}}</td>
</tr>
{% endfor %}
</tbody>
</table>
<input type="submit" value="Update" />
</form>
<style>
select {
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
background: transparent;
border: none;
font-size: 15px;
}
select {
pointer-events: none;
}
</style>
</body>
</html>
Our friend in comments meant that approved and declined fields are mutually exclusive. You only need one of them to indicate that specific status.
class PendingRequest(models.Model):
...
approved = models.BooleanField(default=False)
declined = models.BooleanField(default=False)
...
Related to you question. Form is not validating because id field is required, so add them in:
forms.py
class ApproveForm(forms.ModelForm):
approved = forms.BooleanField(required=False)
class Meta:
model = PendingRequest
fields = [
"id",
"approved",
"book",
]
widgets = {
"id": forms.HiddenInput()
}
and also in templates.py
...
<tbody>
{% for form in formset %}
<tr>
{{form.id}}
<td>{{form.book}}</td>
<td>{{form.approved}}</td>
</tr>
{% endfor %}
</tbody>
...
and views.py
if request.method == "POST":
formset = PendingRequestFormSet(request.POST, queryset=pending_requests)
for form in formset:
if form.is_valid():
if form.cleaned_data['approved']:
form.save()
That will set approved field to True or False on update click. If you want to maintain declined field then just add to forms.pt as a hidden field using the same process, although I would recommend to remove it.
Related
I want chained dropdown list in model formset.
I have 4 models in my app App_Prod - Category, Product, OrderInfo, Order
In my form, I used two views combinely. OrderInfo and Order
So the choice list of product field of the Order model should be dependent on the category field of the OrderInfo model. Like if I select Electronics category the choice list should return only laptop and mobile option instead of showing all. See the image for your better understanding.
Please suggest me what should I edit in my codes, or is there any other way to do so.
models.py
class Category(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Product(models.Model):
name = models.CharField(max_length=100)
price = models.IntegerField()
category = models.ForeignKey(Category, related_name='products', on_delete=models.CASCADE)
def __str__(self):
return self.name
class OrderInfo(models.Model):
category = models.ForeignKey(Category, related_name='orderInfo', on_delete=models.CASCADE)
date = models.DateField()
def __str__(self):
return self.category.name
class Order(models.Model):
info = models.ForeignKey(OrderInfo, related_name='orders', on_delete=models.CASCADE)
product = models.ForeignKey(Product, related_name='productName', on_delete=models.CASCADE, null=True)
quantity = models.IntegerField()
def __str__(self):
return self.product.name
forms.py
class OrderInfoForm(forms.ModelForm):
date = forms.DateField(widget=DateInput)
class Meta:
model = OrderInfo
fields = ["category","date",]
class OrderForm(forms.ModelForm):
class Meta:
model = Order
fields = ["product","quantity",]
widgets = {
'product': forms.Select(attrs={'class': 'formset-field form-control'}),
'quantity': forms.NumberInput(attrs={'class': 'formset-field form-control'}),
}
views.py
def order_create(request):
context = {}
OrderFormset = modelformset_factory(Order, form=OrderForm)
form = OrderInfoForm(request.POST or None)
formset = OrderFormset(request.POST or None, queryset=Order.objects.none(), prefix='productName')
if request.method == "POST":
if form.is_valid() and formset.is_valid():
try:
with transaction.atomic():
info = form.save(commit=False)
info.save()
for order in formset:
data = order.save(commit=False)
data.info = info
data.save()
except IntegrityError:
print("Error Encountered")
return redirect('App_Prod:order_create')
context['formset'] = formset
context['form'] = form
return render(request, 'App_Prod/order_create.html', context)
urls.py
urlpatterns = [
path('create/', views.order_create, name="order_create"),
]
order_create.html
<!DOCTYPE html>
{% extends 'base.html' %}
{% load static %}
{% load crispy_forms_tags %}
{% block body_block %}
<form method="POST">
{% csrf_token %}
{{ form|crispy }}
<table class="table form-table table-bordered table-sm">
<thead class="text-center">
<tr>
<th>Product</th>
<th>Quantity</th>
<th></th>
</tr>
</thead>
<tbody>
{% for form_data in formset %}
<tr class="item">
<td>
{{ form_data.product }}
</td>
<td>
{{ form_data.quantity }}
</td>
<td>
<button type="button" class="btn btn-danger btn-sm remove-form-row"
id="{{ formset.prefix }}">
Delete
</button>
</td>
</tr>
{% endfor %}
<tr>
<td colspan="9"
style="border-left: none!important; border-right: none !important; border-bottom: none!important;">
<button type="button" class="btn btn-sm btn-success add-form-row"
id="{{ formset.prefix }}">
Add Activity
</button>
</td>
</tr>
</tbody>
</table>
{{ formset.management_form }}
<button class="btn btn-info" type="submit">Submit</button>
</form>
{% endblock %}
{% block extra_script %}
<script type="text/javascript" src="{% static 'js/formset.js' %}"></script>
{% endblock%}
I am trying to develop a page in Django where there is multiple form that the user can modify.
For this, I need to get the id of the form the user ask to modify.
There is my code:
models.py:
class Order(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE
)
date = models.DateField()
forms.py:
class OrderForm(ModelForm):
class Meta:
model = Order
fields = ["date"]
views.py:
def order(request, identifiant):
user_orders = []
form_user_orders = []
user = User.objects.get(username=identifiant) #We use identifiant from the url to get the user
user_orders.append(Order.objects.filter(user=user.id)) #We get all order of the user
if request.method == "POST":
form = OrderForm(request.POST)
if form.is_valid():
order_instance = Order(
id = None, #This is where I have a problem
user=User(id=user.id),
date=form.cleaned_data["date"],
)
order_instance.save()
return HttpResponseRedirect(f"/forms/orders/{identifiant}")
else:
for order in user_orders[0]: #We iterate on all orders and put them in another list as form instances
form_user_orders.append(OrderForm(instance=order))
return render(request, "forms/orders.html", {"identifiant": identifiant, "forms": form_user_orders})
urls.py:
urlpatterns = [
path("orders/<identifiant>", views.order)
]
order.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Order</title>
</head>
<body>
{% for form in forms %}
<form method="POST">
{% csrf_token %}
<div>
<label>Date</label>
<div>
{{ form.date }}
</div>
</div>
</form>
{% endfor %}
</body>
</html>
I am trying to work on a django-based project/website that logs who took out our dogs last. I am very new to django so please bear with lack of knowledge in the field. I currently have my project set up so that I have models based on users/family members, the families' dogs, posts (logs of when and who took out the dogs), and actions, which is a child class that uses the dog and post models as foreign keys to see if the dogs peed and or pooped when they were taken out. The part that I am stuck on is trying to figure out how I create the post form. Since we have two dogs I need the form/post page to display a set of check boxes (for peeing and pooping actions) for each dog model that exists. While I can successfully display one action set, I run into the difficulty of trying to display the correct number of action sets and also to post the set of actions and the post itself. I have been trying to work with the formset_factory function but I am confused as to how I get it to function properly. Can anyone help? I have been stuck on this problem for quite a while and any support would be greatly appreciated.
models.py:
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
from django.urls import reverse
class Dog(models.Model):
name = models.CharField(max_length = 20)
class Post(models.Model):
walker = models.ForeignKey(User, on_delete = models.CASCADE)
time_posted = models.DateTimeField(default = timezone.now)
issues = models.TextField(max_length = 300)
class Action(models.Model):
post = models.ForeignKey(Post, on_delete = models.CASCADE)
dog = models.ForeignKey(Dog, on_delete = models.CASCADE)
peed = models.BooleanField(default = False)
pooped = models.BooleanField(default = False)
forms.py:
from django import forms
from django.forms import formset_factory
from dog_log_app.models import *
class Log_Form(forms.ModelForm):
class Meta:
model = Post
fields = ("issues",)
class Action_Form(forms.Form):
peed = forms.BooleanField(initial = False, required = False)
pooped = forms.BooleanField(initial = False, required = False)
views.py:
from django.shortcuts import render
from django.forms import formset_factory, modelformset_factory
from .models import Post, Dog, Action
from dog_log_app.forms import *
from django.contrib.auth.models import User
from django.http import HttpResponse, HttpResponseRedirect
def home(request):
context = {
'posts': Post.objects.all().order_by('-time_posted'),
'actions': Action.objects.all(),
'dogs': Dog.objects.all()
}
return render(request, 'dog_log_app/home.html', context)
def post(request):
form_post = Log_Form(request.POST or None)
form_actions = modelformset_factory(Action, fields = ('peed', 'pooped'), extra = Dog.objects.count())
if request.method == 'POST':
form_post = Log_Form(request.POST)
if form_post.is_valid() and form_actions.is_valid():
post_save = form_post.save(commit = False)
post_save.walker = request.user
post_save.save()
form_actions.save()
return HttpResponseRedirect('..')
context = {
'post': form_post,
'action': formset_factory(Action_Form, extra = Dog.objects.count()),
'dogs': Dog.objects.all()
}
return render(request, 'dog_log_app/post_form.html', context)
post_form.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Post</title>
</head>
<body>
<div>
<form method="POST">
{% csrf_token %}
<fieldset>
<legend>Create Post</legend>
<h3>{{dogs.name}}</h3>
<p>{{action.as_p}}</p>
<p>{{post.as_p}}</p>
</fieldset>
<div>
<button type="submit" value="Ok">Post</button>
</div>
</form>
</div>
</body>
</html>
what you can try would be to use ajax to add the amount you want, what I usually do is use the form:
from django.forms import ModelForm
from django.forms.models import inlineformset_factory
class PostForm(ModelForm):
class Meta:
model = Post
fields = (
'issues',
)
class ActionForm(ModelForm):
class Meta:
model = Action
fields = (
'peed', 'pooped'
)
ActionFormSet = inlineformset_factory(
Post, Action, form=ActionForm,
fields = ['peed', 'pooped'], extra=1, can_delete=True,
)
With the forms created, we move on to the views, which will be class-based for convenience:
from django.db import transaction
from django.views.generic import CreateView, UpdateView
from .models import Post
from .forms import PostForm, ActionFormSet
class PostCreateView(CreateView):
template_name = 'your template'
form_class = PostForm
success_url = reverse_lazy('your url')
def get_context_data(self, **kwargs):
data = super().get_context_data(**kwargs)
if self.request.POST:
data['formset'] = ActionFormSet(self.request.POST)
else:
data['formset'] = ActionFormSet()
return data
def form_valid(self, form):
context = self.get_context_data()
formset = context['formset']
with transaction.atomic():
self.object = form.save()
if formset.is_valid():
formset.instance = self.object
formset.instance.walker = self.request.user
formset.save()
return super().form_valid(form)
class PostUpdateView(UpdateView):
template_name = 'your template'
form_class = PostForm
model = Post
success_url = reverse_lazy('your url')
def get_context_data(self, **kwargs):
data = super().get_context_data(**kwargs)
if self.request.POST:
data['formset'] = ActionFormSet(
self.request.POST, instance=self.object)
else:
data['formset'] = ActionFormSet(instance=self.object)
return data
def form_valid(self, form):
context = self.get_context_data()
formset = context['formset']
with transaction.atomic():
self.object = form.save()
if formset.is_valid():
formset.instance = self.object
formset.save()
return super().form_valid(form)
and to display the information you can use a jquery library for the dynamic information on saving:
enter link description here
With that the configuration is much easier
An example template would be:
{% extends "base.html" %}
{% load static %}
{% block content %}
<div class="container">
<div class="card">
<div class="card-content">
<h2>Agregar Proyecto</h2>
<div class="col-md-4">
<form id="form-container" method="post">
{% csrf_token %}
{{ form }}
<h2>Actions</h2>
<table>
{{ formset.management_form }}
{% for form in formset %}
{% if forloop.first %}
<thead>
<tr>
{% for field in form.visible_fields %}
<th>{{ field.label|capfirst }}</th>
{% endfor %}
</tr>
</thead>
{% endif %}
<tr id="projects_data">
{% for field in form.visible_fields %}
<td>
{# Include the hidden fields in the form #}
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{{ field.errors.as_ul }}
{{ field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<button type="submit" class="btn btn-success">{{ message }}</button>
Cancel
</form>
</div>
</div>
</div>
</div>
{% endblock %}
{% block script %}
<script type="text/javascript">
$(function () {
$('#projects_data').formset({
prefix: '{{ formset.prefix }}',
addText: 'create',
deleteText: 'remove',
});
})
</script>
{% endblock %}
and in that way add data dynamically.
I am using django 2.2 and python 3.6.
I have a django modelform. I am using crispy forms to display the form in template.
The form is not displaying the value of the records. It is displaying the uuid values of the records. I need to display the record name value instead of uuid value.
models.py :
#reversion.register()
class Staff(BaseModel):
user = models.OneToOneField(
User, on_delete=models.PROTECT, db_index=True, verbose_name=_("Kullanıcı"))
photo = models.ImageField(
upload_to="staff/", null=True, blank=True, verbose_name=_("Fotoğraf"))
staff_type = models.ManyToManyField(
StaffType, verbose_name=_("Personel Tipi"))
name = models.CharField(
max_length=100, db_index=True, verbose_name=_("İsim"))
surname = models.CharField(
max_length=100, db_index=True, verbose_name=_("Soyad"))
phone = models.CharField(max_length=100, verbose_name=_("Telefon Numarası"))
email = models.EmailField(verbose_name=_("Email"), db_index=True)
address = models.TextField(verbose_name=_("Adres"))
subject = models.ForeignKey(Subject, on_delete=models.SET(
get_unknown_subject), verbose_name=_("Branş"))
gender = models.IntegerField(
choices=GENDERS, default=None, verbose_name=_("Cinsiyet"))
nationality = models.CharField(
choices=NATIONALITIES, max_length=100, verbose_name=_("Uyruk"))
blood_type = models.CharField(
choices=BLOOD_TYPES, max_length=20, verbose_name=_("Kan Grubu"))
id_no = models.CharField(max_length=100, unique=True,
verbose_name=_("Kimlik No"))
birthdate = models.DateField(verbose_name=_("Doğum Günü"))
birthplace = models.CharField(max_length=200, verbose_name=_("Doğum Yeri"))
education = models.IntegerField(
choices=EDUCATION, default=None, verbose_name=_("Eğitim"))
marital_status = models.BooleanField(
default=True, verbose_name=_("Evlilik Durumu"))
number_of_children = models.IntegerField(
verbose_name=_("Çocuk Sayısı"))
special_notes = models.TextField(
null=True, blank=True, verbose_name=_("Özel Notlar"))
registration_date = models.DateField(verbose_name=_("Kayıt Tarihi"))
foreign_language = models.ForeignKey(Language, on_delete=models.SET(
get_default_language), null=True, blank=True, verbose_name=_("Yabancı Dil"))
class Meta:
permissions = (
("list_staff", _("List Staff")),
)
ordering = ['name', 'surname']
def __unicode__(self):
return "%s %s" % (self.name, self.surname)
def save(self, *args, **kwargs):
self.name = self.name.title()
self.surname = self.surname.upper()
groups = []
for staff_type_object in self.staff_type.all():
group = Group.objects.get_or_create(name=staff_type_object.name)[0]
groups.append(group)
self.user.groups = groups
self.user.save()
super(Staff, self).save(*args, **kwargs)
views.py:
#login_required(login_url='/accounts/login/')
def personelkayit(request):
staffs = Staff.objects.values("user__username", "name", "surname", "uuid")
if request.method == 'GET':
staff_uuid = request.GET.get("staff_uuid")
if staff_uuid:
instance = get_object_or_404(Staff, uuid=staff_uuid)
form = StaffForm(instance=instance)
form.fields['username'].initial = instance.user.username
form.fields['username'].widget.attrs['readonly'] = True
else:
form = StaffForm()
return render(request, 'personelkayit.html', {'form': form, 'staffs':
staffs, 'staff_uuid': staff_uuid})
elif request.method == 'POST':
staff_uuid = request.GET.get("staff_uuid")
if staff_uuid:
instance = get_object_or_404(Staff, uuid=staff_uuid)
form = StaffForm(request.POST or None,
request.FILES or None, instance=instance)
else:
form = StaffForm(request.POST, request.FILES or None)
if form.is_valid():
password = form.cleaned_data.get("password")
re_password = form.cleaned_data.get("re_password")
staff = form.save(commit=False)
staff.user = user
staff.save()
form.save_m2m()
staff.save()
return redirect('/')
else:
return render(request, "personelkayit.html", {'form': form, 'staffs': staffs})
personelkayit.html:
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% load i18n %}
{% load staticfiles %}
{% block content %}
<br>
<table style="width: 100%">
<tr>
<td style="width: 4%"></td>
<td style="width: 92%">
<div class="block-area" id="basic">
<form role="form" method="get">
<select class="form-control input-sm m-b-10" name="staff_uuid">
<option value="" {% if not staff_uuid %} selected="selected" {% endif %} disabled="disabled">{% trans "Personel" %}</option>
{% for staff in staffs %}
<option {% if staff_uuid and staff.uuid|lower == staff_uuid|lower %}selected="selected"{% endif %} value="{{ staff.uuid }}">{{ staff.user__username }} - {{ staff.name }} {{ staff.surname }}</option>
{% endfor %}
</select>
<br />
<button type="submit" class="btn btn-sm btn-alt">{% trans "Personel Düzenle" %}</button>
<a class="btn btn-sm btn-alt" href="{% url 'personelkayit' %}">{% trans "Personel Ekle" %}</a>
<div class="modal-footer"></div>
</form>
</div>
<div class="block-area" id="basic">
<form role="form" enctype="multipart/form-data" method="post">
{% csrf_token %}
{{ form|crispy }}
<div >
<br>
<button style="background-color: #002266 !important; color: white !important" type="submit" class="btn btn-lg btn-alt"><span class="glyphicon glyphicon-floppy-disk
pull-left"></span>{% trans "Kaydet" %}</button>
</div>
</form>
<br>
</div>
</td>
<td style="width: 4%"></td>
</tr>
</table>
<script>
$( document ).ready(function() {
$( "select[name$='staff_uuid']" ).select2({
theme: "bootstrap"
});
$( "select[name$='subject']" ).select2({
theme: "bootstrap"
});
$( "select[name$='staff_type']" ).select2({
theme: "bootstrap"
});
});
</script>
{% endblock content %}
The reason this happens is because Django will render the string-representation of model objects in the form. A model is, by default rendered by the name of the type of the object, and the primary key. So a string like Subject object (...) is normal.
You can simply implement a __str__ (and for python-2.x a __unicode__) for the Subject model, to render it the way you specify:
class Subject(models.Model):
# …
def __str__(self):
return …
I'm making an answering/commenting system for my post's. And here's how the models.py looks:
from django.conf import settings
from django.core.urlresolvers import reverse
from django.db import models
from django.utils import timezone
import misaka
from groups.models import Group
from django.contrib.auth import get_user_model
User = get_user_model()
class Post(models.Model):
user = models.ForeignKey(User, related_name="posts")
created_at = models.DateTimeField(auto_now=True)
message = models.TextField()
message_html = models.TextField(editable=False)
group = models.ForeignKey(Group, related_name="posts",null=True, blank=True)
def __str__(self):
return self.message
def save(self, *args, **kwargs):
self.message_html = misaka.html(self.message)
super().save(*args, **kwargs)
def get_absolute_url(self):
return reverse(
"posts:single",
kwargs={
"username": self.user.username,
"pk": self.pk
}
)
class Meta:
ordering = ["-created_at"]
unique_together = ["user", "message"]
class Answer(models.Model):
post = models.ForeignKey('posts.Post', related_name='posts')
author = models.ForeignKey(User, related_name='answers')
answer = models.TextField()
create_date = models.DateTimeField(default=timezone.now)
published_date = models.DateTimeField(blank=True, null=True)
def __str__(self):
return self.answer
def get_absolute_url(self):
return reverse('post_detail', kwargs={
"username": self.user.username,
"pk": self.pk,
})
I've been having a problem, with basically showing the answers/comment's in the post detail. Here's how my post detail looks:
<!DOCTYPE html>
{%load staticfiles%}
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js" integrity="sha384-alpBpkh1PFOepccYVYDB4do5UnbKysX5WZXm3XxPqe5iKTfUKjNkCk9SaVuEZflJ" crossorigin="anonymous"></script>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="keywords" content="footer, address, phone, icons" />
<link href="https://fonts.googleapis.com/css?family=Abel|Raleway|Signika|Signika+Negative" rel="stylesheet">
<title>Ask Uptown</title>
<style media="screen">
.logo-text span{
color: #5383d3;
}
.backgr{
background-color: #7F7F7F;
height: 13vh;
width: 100%;
background-size: cover;
background-position: center;
}
header{
background: src(.backgr);
}
.no-ans{
position: relative;
top: 50px;
left: 50px;
font-size: 20px;
}
.add-comment{
position: relative;
text-decoration: none;
z-index: 99;
position: relative;
display: flex;
justify-content: center;
align-items: center;
width: 300px;
height: 40px;
background: linear-gradient(45deg, #B388EB, #8093F1);
border-radius: 20px;
color: #FFF;
font-size: 20px;
letter-spacing: 1px;
font-weight: 200;
left: 1100px;
text-decoration: none;
left: 800px;
top: 75px;
}
</style>
</head>
<body>
<header>
<div class="backgr">
<nav class="" role="navigation" id="navbar">
<div class="">
<div class="logo">
<a class="logo-text" href="{% url 'home' %}"><h3>Uptown<span>Ask</h3></span></a>
</div>
<ul class="main-nav">
{% if user.is_authenticated %}
<li class="active">Home</li>
<li>Stuck? Ask A question</li>
<li>Groups</li>
<li>Contact</li>
<li>Log out</li>
{% else %}
<li class="active">Home</li>
<li>Groups</li>
<li><a href="{% url 'accounts:login' %}" >Log in</a></li>
<li><a href="{% url 'accounts:signup' %}" >Sign up</a></li>
<li>Contact</li>
{% endif %}
</ul>
</header>
<section>
{% block post_content %}
<div class="col-md-8">
{% include "posts/_post.html" %}
</div>
{% endblock %}
<a class="add-comment" href="{% url 'posts:add_comment_to_post' pk=post.pk%}">Answer This Question</a>
{% for comment in post.comments.all %}
<br>
{% if user.is_authenticated %}
{{ comment.created_date }}
{% if not comment.approved_comment %}
<a class="btn btn-default" href="{% url 'comment_remove' pk=comment.pk %}"><span class="glyphicon glyphicon-remove"></span></a>
<a class="btn btn-default" href="{% url 'comment_approve' pk=comment.pk %}"><span class="glyphicon glyphicon-ok"></span></a>
{% endif %}
<p>{{ comment.text|safe|linebreaks }}</p>
<p>Posted by: <strong>{{ comment.author }}</strong></p>
{% endif %}
{% empty %}
<p class="no-ans">No Answer's posted.</p>
{% endfor %}
</div>
</section>
<footer class="footer-distributed">
<div class="footer-left">
<h3>Uptown<span>Ask</span></h3>
<p class="footer-links">
Home
·
Blog
·
About
·
Contact
</p>
<p class="footer-company-name">Ask Uptown © 2017</p>
</div>
<div class="footer-center">
<div class="adress-sec">
<p class="p-adress">Address: Tripoli Street, Algeria Road, Mirdif Area - Dubai, United Arab Emirtes</p>
</div>
<div class="phone-sec">
<p>Phone: 04 251 5001</p>
</div>
<div class="email-sec">
<p class="p-email">Email: Uptownschool#gmail.com</p>
</div>
</div>
<div class="footer-right">
<p class="footer-company-about">
<span>About the company</span>
Lorem ipsum dolor sit amet, consectateur adispicing elit. Fusce euismod convallis velit, eu auctor lacus vehicula sit amet.
</p>
<div class="icons">
<img src="images/twitter.png" alt="Twitter" class="twitter" >
<img src="facebook.png" alt="Facebook" class="facebook" >
<img src="insta.png" alt="Instagram" class="instagram" >
</div>
</div>
</footer>
</body>
</html>
Here's an image of how it looks on the actual site. PIcture of how the post detail looks on the actual site
but I'm having a problem showing the comment's/answer's when they are posted. If I use the admin to post it doesn't show. and if I click on the answer this question button it gives me this error: Picture for error
Here are my views and urls.py:
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.urlresolvers import reverse_lazy
from django.http import Http404
from django.views import generic
from braces.views import SelectRelatedMixin
from . import forms
from . import models
from django.contrib.auth import get_user_model
User = get_user_model()
class PostList(SelectRelatedMixin, generic.ListView):
model = models.Post
select_related = ("user", "group")
class UserPosts(generic.ListView):
model = models.Post
template_name = "posts/user_post_list.html"
def get_queryset(self):
try:
self.post_user = User.objects.prefetch_related("posts").get(
username__iexact=self.kwargs.get("username")
)
except User.DoesNotExist:
raise Http404
else:
return self.post_user.posts.all()
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["post_user"] = self.post_user
return context
class PostDetail(SelectRelatedMixin, generic.DetailView):
model = models.Post
select_related = ("user", "group")
def get_queryset(self):
queryset = super().get_queryset()
return queryset.filter(
user__username__iexact=self.kwargs.get("username")
)
class CreatePost(LoginRequiredMixin, SelectRelatedMixin, generic.CreateView):
# form_class = forms.PostForm
fields = ('message','group')
model = models.Post
# def get_form_kwargs(self):
# kwargs = super().get_form_kwargs()
# kwargs.update({"user": self.request.user})
# return kwargs
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.user = self.request.user
self.object.save()
return super().form_valid(form)
class DeletePost(LoginRequiredMixin, SelectRelatedMixin, generic.DeleteView):
model = models.Post
select_related = ("user", "group")
success_url = reverse_lazy("posts:all")
def get_queryset(self):
queryset = super().get_queryset()
return queryset.filter(user_id=self.request.user.id)
def delete(self, *args, **kwargs):
messages.success(self.request, "Post Deleted")
return super().delete(*args, **kwargs)
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 = forms.CommentForm()
return render(request, 'posts/comment_form.html', {'form': form})
def comment_remove(request, pk):
comment = get_object_or_404(Comment, pk=pk)
post_pk = comment.post.pk
comment.delete()
return redirect('post_detail', pk=post_pk)
The comment_remove function based view is for users to delete their comment/answer. and the add_comment_to_post is the class that posts the comment/answer.
from django.conf.urls import url
from . import views
app_name='posts'
urlpatterns = [
url(r"^$", views.PostList.as_view(), name="all"),
url(r"new/$", views.CreatePost.as_view(), name="create"),
url(r"by/(?P<username>[-\w]+)/$",views.UserPosts.as_view(),name="for_user"),
url(r"by/(?P<username>[-\w]+)/(?P<pk>\d+)/$",views.PostDetail.as_view(),name="single"),
url(r"delete/(?P<pk>\d+)/$",views.DeletePost.as_view(),name="delete"),
url(r'^post/(?P<pk>\d+)/comment/$', views.add_comment_to_post, name='add_comment_to_post'),
url(r'^comment/(?P<pk>\d+)/remove/$', views.comment_remove, name='comment_remove'),
]
I'cant seem to find a way to show the comment's under the post detail, plzz help if your a django genius.
Change:
post = models.ForeignKey('posts.Post', related_name='posts')
To
post = models.ForeignKey('posts.Post', related_name='comments')
So that your template’s ‘post.comments.all’ works