Django learner here, I am trying to build a simple blog website in which i have created two models:
one is Post:
class Post(models.Model):
title = models.CharField(max_length=255)
author= models.ForeignKey(User, null=True, blank=True , on_delete=models.CASCADE)
article = models.TextField()
created_on = models.DateTimeField(auto_now_add=True)
slug = AutoSlugField(populate_from='title', unique=True, null=True, default=None)
category = models.ForeignKey(Category, null=True, blank=True, on_delete=models.CASCADE )
def __str__(self):
return self.title
second is category:
class Category(models.Model):
categories = models.CharField(max_length=24, blank=True)
def __str__(self):
return self.categories
all i am trying to do is to show Category on home page, and when someone click on any category it will open up all the post related to that category.
This is home.html :
{% extends 'blog_pages/base.html' %}
{% block content %}
<div class = "container p-3">
<h3> This is your home page</h3>
</div>
<div class = "container p-1">
<table class="table table-hover table-bordered">
<thead>
<tr>
<th scope="col">Categories</th>
<th scope="col">About</th>
</tr>
</thead>
<tbody>
{% for c in cat %}
<tr>
<th scope="row"><a href="{% url 'all_articles' c %}" ><p> {{c}}</P></a></th>
<td> How you can win in life</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
this is views.py :
def home(request):
cat = Category.objects.all()
return render(request, 'blog_pages/home.html',{'cat': cat})
def all_articles(request, c):
post = Post.objects.filter(category__contains = c).values()
return render(request,"blog_pages/all_articles.html",{'post':post})
I am getting this error " FieldError(
django.core.exceptions.FieldError: Related Field got invalid lookup: contains"
i have tried many possible ways to solve this problem, but nothing works.
Related
I would like to have dropdowns filters for users browsing their book collection. The dropdown values are currently populated with every corresponding field value in the model, I only want users to get values relevant to them e.g. I have only publisher associatted to my books, 'Marvel', so I should only see Marvel in the publisher drop down when I go to filter my books.
I am not able to pass the user value to the form drop downs, even after setting up the initialization function. I keep getting error no such attribute as 'uid' or'user' in the view when I am passing the value to the form.
Models.py
class ComicInput(models.Model):
Publisher = models.CharField(max_length=20, default='Marvel', choices=Publisher_Choices, null=True, blank=True )
Title = models.CharField(max_length=50,default='', blank=False)
Type = models.CharField(max_length=30, choices=Type_Choices, null=True, blank=True ) #default='Reg'
Number = models.IntegerField(default='', blank=False)
Category = models.CharField( max_length=12,default="Hold",choices=Category_Choices,blank=True, null=True)
uid = models.ForeignKey(User,on_delete=models.CASCADE, editable=False) #default=False, null=True)
def __unicode__(self):
return '%s %s %s' % (self.uid,self.Title, self.Number, self.Grade, self.Series, self.CoverPic, self.Category)
class Meta:
ordering = ('Title', 'Series', 'Number')
Views.py
###################### Collection Viewer #############
#login_required(login_url="/login/")
def ComicInventory(self):
title_list = TitleChoiceField()
publisher_list = PublisherChoiceField()
sellingnotes_list = NotesChoiceField()
category_list = CategoryChoiceField()
if self.GET.get('titles'): # On screen drop down Filter for titles
selected_title = self.GET.get('titles')
displayInventory=ComicInput.objects.filter(Title=selected_title,uid=self.user)
DisplaySumValue=ComicInput.objects.all().filter(Title=selected_title,uid=self.user).aggregate(Sum('Value'))
else:
displayInventory=ComicInput.objects.filter(uid=self.user)
DisplaySumValue=ComicInput.objects.all().aggregate(Sum('Value'))
context = {
'displayInventory': displayInventory,
'DisplaySumValue': DisplaySumValue,
'title_list': title_list,
}
return render(self, 'app/viewer.html',context)
HTML
<body>
<h1><Strong>Here are your comics;</Strong></h1>
<div class="panel-heading">
**<!.. this is the Choice Field on HTML ..!>**
<div class="panel-title pull-left">
<form method="get" action="{% url 'ComicInventory' %}">
{{ category_list }}
<input type="submit" value="Filter">
</form>
</div>
<div class="container">
<table class="table table-striped">
<thead class="thead-dark">
<tr>
<th scope="col">Publisher</th>
<th scope="col">Title</th>
<th scope="col">Number</th>
<th scope="col">Edition</th>
</tr>
</thead>
{% for inv in displayInventory %}
<tbody class="table table-hover">
<tr>
<td>{{inv.Publisher}}</td>
<td>{{inv.Title}}</td>
<td>{{inv.Number}}</td>
<td>{{inv.Edition}}</td>
alt="{{inv.Publisher}} image",height="60", width="100" /></a></td>
<td> Edit </td>
<td> Delete </td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<td><b>Total Value: {{DisplaySumValue}} </b></td>
</tr>
</tfoot>
</table>
</div>
</body>
EDIT
Form.py
##Updated Model ChoiceField that initiates self, so I can get the user and pass it to the view ##
class TitleChoiceField(forms.Form):
class Meta:
model = ComicInput
fields = ('Title', 'uid',)
def __init__(self,uid, *args, **kwargs):
super(TitleChoiceField, self).__init__(*args, **kwargs)
self.fields['titles'].queryset=ComicInput.objects.filter(uid=self.user).values_list("Title", flat=True).distinct().order_by('Title')
Django AttributeError: Form object has no attribute '_errors'
Updated the forms like so based on the above post:
Forms.py
class TitleChoiceField(forms.Form):
class Meta:
model = ComicInput
fields = ('Title','uid',)
titles = forms.ModelChoiceField(queryset =ComicInput.objects.all())
def __init__(self, uid=None, *args, **kwargs):
super(TitleChoiceField, self).__init__(*args, **kwargs)
self.user = uid
usrqry = ComicInput.objects.filter(uid=self.user).values_list('Title', flat=True).distinct().order_by('Title')
self.fields['titles'].queryset=usrqry
I am relatively new to Django but the main problem I am facing right now is to create a ListView that will display uploaded documents based on the primary key of my ForeignKey.
I have tried several methods of trying to create the filter and read the online documentation on class-based view but it does not seem to have relevant information on how to use the primary key of my ForeignKey in my filter.
These are my models:
class Post(models.Model):
title = models.CharField(max_length=100)
image = models.ImageField(default = 'default0.jpg',
upload_to='course_image/')
description = models.TextField()
price = models.DecimalField(decimal_places=2, max_digits=6)
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
rating = models.IntegerField(default = 0)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk' : self.pk})
class Lesson(models.Model):
title = models.CharField(max_length=100)
file = models.FileField(upload_to="lesson/pdf")
date_posted = models.DateTimeField(default=timezone.now)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('lesson_upload', kwargs={'pk': self.pk})
Here is my ListView with the filter that is not working:
class LessonListView(ListView):
model = Lesson
template_name = 'store/uploaded_lesson.html'
context_object_name = 'lesson'
# def get_queryset(self):
# return Lesson.objects.filter(Post__pk=self.Post.pk)
def get_queryset(self):
self.post__pk = get_object_or_404(post__pk,
name=self.kwargs['post__pk'])
return Lesson.objects.filter(post__pk=self.post__pk)
Here is my urls.py:
path('post/<int:pk>/lesson_uploaded/', LessonListView.as_view(), name='lesson_uploaded'),
Here is my html:
{% extends "store/base.html" %}
{% block content %}
<div id="main">
<table class="table mb-0">
<thead>
<tr>
<th>Title</th>
<th>Author</th>
<th>Download</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
{% for lesson in lesson %}
<tr>
<td>
{% if lesson.file %}
<img src="{{ lesson.file.url }}" style="width:100px;">
{% else %}
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}
You can try like this:
In urls, add post_id :
path('lessons/<int:post_id>/', LessonListView.as_view()),
Then update the View to get the post_id in get_queryset method:
class LessonListView(ListView):
model = Lesson
template_name = 'store/uploaded_lesson.html'
context_object_name = 'lesson'
def get_queryset(self):
return Lesson.objects.filter(post_id=self.kwargs.get('post_id'))
Also, please don't name list and item of that list in a for loop same, so update it to:
{% for l in lesson %}. // or anything other than lesson
<tr>
<td>
{% if l.file %}
Basically I am trying to create an app which can allow user to upload lessons material such as documents on their specific posts. However, I am unsure of how I should display the documents uploaded based on the primary key of the posts. As of now, my posts are displaying all the documents that are being uploaded by the particular user.
This is my 'Post' model
class Post(models.Model):
title = models.CharField(max_length=100)
image = models.ImageField(default = 'default0.jpg', upload_to='course_image/')
description = models.TextField()
price = models.DecimalField(decimal_places=2, max_digits=6)
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
rating = models.IntegerField(default = 0)
def __str__(self):
return self.title
This is my 'Lesson' model
class Lesson(models.Model):
title = models.CharField(max_length=100)
file = models.FileField(upload_to="lesson/pdf")
date_posted = models.DateTimeField(default=timezone.now)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('lesson_upload', kwargs={'pk': self.pk})
This is my Class-based listview that is supposed to display the uploaded lessons based on the posts:
class LessonListView(ListView):
model = Lesson
template_name = 'store/uploaded_lesson.html'
context_object_name = 'lesson'
def get_queryset(self):
self.post = get_object_or_404(post, name=self.kwargs['post'])
return Lesson.objects.filter(post=self.post)
{% extends "store/base.html" %}
{% block content %}
<div id="main">
<table class="table mb-0">
<thead>
<tr>
<th>Title</th>
<th>Author</th>
<th>Download</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
{% for lesson in lesson %}
<tr>
<td>
{% if lesson.file %}
<img src="{{ lesson.file.url }}" style="width:100px;">
{% else %}
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}
You will need another model for the material they are uploading. Something like:
class UploadedPostMaterial(models.Model):
content= models.TextField()
date_uploaded = models.DateTimeField(default=timezone.now)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
This model will contain the uploaded data and tie it to a post. Now when you display all or specific Posts in your app, you can select the related content for each post and display the uploaded content.
I am doing a simple invoice app and i encountered a problem with my django model form. Form is showing all of the ManytoManyfields objects but i would like to display all of their fields not just an objectname.
My models.py:
from django.db import models
import datetime
from django.urls import reverse
# Create your models here.
class Product(models.Model):
item_name = models.CharField(max_length=100)
chinese_name = models.CharField(max_length=100)
erp_number = models.CharField(max_length=20)
unit = models.CharField(max_length=8)
photo = models.ImageField(blank=True, null=True)
price_usd = models.DecimalField(max_digits=8, decimal_places=2)
price_eur = models.DecimalField(max_digits=8, decimal_places=2)
def __str__(self):
return self.item_name
class Batch(models.Model):
batch_name = models.CharField(max_length=100)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
quantity = models.DecimalField(max_digits=5, decimal_places=0)
def __str__(self):
return self.batch_name
class Invoice(models.Model):
invoice_no = models.CharField(max_length=30, default="Proforma Invoice 2018102300001")
products = models.ManyToManyField(Product)
total = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True)
date = models.DateField(auto_now_add=False,auto_now=False, default=datetime.datetime.now())
def __str__(self):
return self.invoice_no
def get_absolute_url(self):
return reverse('invoice_detail',kwargs = {'pk':self.pk})
My forms.py:
from django import forms
from django.forms import modelformset_factory, inlineformset_factory
from .models import Invoice, Product
from django.forms.widgets import CheckboxSelectMultiple
class InvoiceForm(forms.ModelForm):
invoice_no =forms.CharField(widget=forms.TextInput(attrs={'size':40}),initial='Proforma Invoice 2018102300001')
date = forms.DateField(widget = forms.SelectDateWidget)
class Meta:
model = Invoice
fields = '__all__'
def __init__(self, *args, **kwargs):
super(InvoiceForm, self).__init__(*args, **kwargs)
self.fields["products"].widget = CheckboxSelectMultiple()
self.fields["products"].queryset = Product.objects.all()
My template:
{% extends 'base.html' %}
{% block content %}
<!DOCTYPE html>
<html lang="en">
<body>
<form method="POST" action="">
{% csrf_token %}
<div clas="row">
<p style="width:20px">{{ form.invoice_no }}</p>
<p>{{ form.date }}</p>
</div>
<div class ="row">
<div class="col-md-12">
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th scope="col" >Photo</th>
<th scope="col">Name</th>
<th scope="col">ERP number</th>
<th scope="col">Unit</th>
<th scope="col">Price (USD)</th>
<th scope="col">Price (EUR)</th>
</tr>
</thead>
<tbody>
{% for product in form.products.all %}
<tr>
<th scope="row"></th>
<td>{{product.item_name}}</td>
<td>{{form.products.erp_number}}</td>
<td>product.unit</td>
<td>product.price_usd</td>
<td>product.price_eur</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<input type="submit" value="Submit">
</form>
</body>
</html>
{% endblock %}
As you can see i am trying to access the fields of Product model by using {% for product in form.products.all %} by it is not working. I've been looking for solution on stackoverflow but haven't found the right answer. Is the a way to display not just an objectname and still keep the self.fields["products"].widget = CheckboxSelectMultiple() widget ?
Following the suggestions from my last post I got this far:
Post model:
class Post(models.Model):
title = models.CharField(max_length=120)
content = models.TextField()
Group model:
class Group(models.Model):
title = models.CharField(max_length=200)
url = models.URLField(unique=True)
contact_updated = models.DateField(auto_now=False, auto_now_add=True)
group_status = models.CharField(max_length=20)
admin = models.CharField(max_length=20)
admin_status = models.CharField(max_length=20)
frequency = models.IntegerField() # allowed post frequency
frq_scale = models.CharField(max_length=20, blank=True)
obs = models.TextField(blank=True)
posts = models.ManyToManyField(Post, through='control.Control')
Control model:
class Control(models.Model):
published = models.DateField(auto_now=False, auto_now_add=False)
post = models.ForeignKey('posts.Post', on_delete=models.CASCADE)
group = models.ForeignKey('groups.Group', on_delete=models.CASCADE)
This is control for posts in groups. I can have 1 post published in many groups controlled from Control model.
CORRECTION:
It is possible for a Post to be published in many groups.
How can I produce the table (link above) with those models? Or perhaps there is something I need to change?
The table I want to produce
class Control(models.Model):
published = models.DateField(auto_now=False, auto_now_add=False)
post = models.ForeignKey('posts.Post', on_delete=models.CASCADE)
group = models.ForeignKey('groups.Group', on_delete=models.CASCADE)
class Meta:
unique_together = (post, group )
I ended up creating a dictionary in the view to be passed to the template.
I haven't changed the models.
This is the view:
def control_list(request):
group_status = STATUS_LIST
group_query_idx = 1
period_initial = date.today()-timedelta(days=30)
period_final = date.today()
if request.method == "POST":
filter_form = FilterControl(request.POST)
if filter_form.is_valid():
group_query_idx = int(filter_form.cleaned_data['group_status'])
period_initial = filter_form.cleaned_data['period_initial']
period_final = filter_form.cleaned_data['period_final']
else:
filter_form = FilterControl()
if group_query_idx:
filtered_groups = Group.objects.filter_by_status(group_status[group_query_idx])
queryset_list = Control.objects.filter_by_group_status(group_status[group_query_idx])\
.filter(published__range=[period_initial, period_final])
query = request.GET.get("q")
if query:
queryset_list = queryset_list.filter(
Q(post__content__icontains=query) |
Q(post__title__icontains=query) |
Q(group__title__icontains=query) |
Q(group__admin__icontains=query) |
Q(group__obs__icontains=query)
).distinct() # avoid duplicated items
controls_per_group = {}
for group in filtered_groups:
control = queryset_list.filter(group_id=group.id)
controls_per_group[group.title] = control
context = {
"object_list": queryset,
"title": "Control",
"controls_per_group": controls_per_group,
"column": range(10),
"group_status": group_status,
"filter_form": filter_form,
}
return render(request, "control_list.html", context)
And this is the template:
<table class="table table-hover table-striped">
<thead class="thead-inverse">
<tr>
<th class="text-center">Action</th>
<th class="text-center">Group</th>
{% for value in column %}
<th class="text-center">#</th>
{% endfor %}
</tr>
</thead>
{% for key, value in controls_per_group.items %}
<tr>
<td class="text-center"><a class='btn btn-info btn-xs disabled' href="#"><i class="fa fa-pencil"></i></a>
<i class="fa fa-trash-o"></i></td>
<th class="text-center">{{ key }}</th>
{% for control in value %}
<th class="text-center">{{ control.published | date:"d/m/y" }}<br>{{ control.post.id }}</th>
{% endfor %}
</tr>
{% endfor %}