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 %}
Related
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.
I am getting confused with the usage of related_name in django models, i know that the idea is to give me access to all the fields of a different table with foreignkey i am just not sure how to use it.
My model.py:
class Component(MPTTModel):
name = models.CharField(max_length=100)
manufacturer = models.CharField(max_length=100)
model = models.CharField(max_length=100)
serial_number = models.CharField(max_length=255)
price = models.IntegerField()
note = models.TextField()
image = models.ImageField(blank=True, null=True,
upload_to='components_imgs')
parent = TreeForeignKey("self", verbose_name=(
"Parent Component"), blank=True, null=True, related_name='children', on_delete=models.CASCADE)
def __str__(self):
return f"{self.id}, {self.name}"
class Job(models.Model):
job_type = (
('I', 'Interval'),
('O', 'One time'),
)
name = models.CharField(max_length=100)
description = models.CharField(max_length=100)
type = models.CharField(max_length=1, choices=job_type)
interval = models.IntegerField()
is_critical = models.BooleanField()
due_date = models.DateField()
component = models.ForeignKey(
Component, related_name='jobs', on_delete=models.CASCADE)
runninghours = models.ForeignKey(
RunningHours, related_name="RHjobs", on_delete=models.CASCADE)
def __str__(self):
return self.name
my view.py:
def worklist(request):
components = Component.objects.all()
Component_jobs = components.jobs.all()
context = {"component":components,
"Component_jobs":Component_jobs}
return render(request,"worklist.html",context)
I am trying to understand why these lines give me an error 'TreeQuerySet' object has no attribute 'jobs'
components = Component.objects.all()
Component_jobs = components.jobs.all()
but these lines work just fine,
component = Component.objects.all()
component_id = Component.objects.get(id=pk)
job_id = component_id.jobs.all()
are they not the same but with one i am getting all the jobs for a specific ID and the other i am getting all the jobs for all components?
Edited,
My template :
<div class="worklist">
{%for component in component %}
<p> {{component.name}} </p>
<div class="runninghours-table-flex">
<table class="runninghours-table">
<thead>
<tr>
<th>Job name</th>
<th>Job type</th>
<th>Due Date </th>
<th>Interval</th>
<th>Rank</th>
<th>Execute Job</th>
</tr>
</thead>
<tbody>
{% for c_jobs in component_jobs%}
<tr>
<td>{{c_jobs.name}} </td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
{%endfor%}
</tbody>
</table>
</div>
{%endfor%}
</div>
Assuming you want to display all components in a loop and then display all related jobs for each component in a nested loop, your template should look something like this simplified example
{% for component in components %}
{{ component }}
{% for job in component.jobs.all %}
{{ job }}
{% endfor %}
{% endfor %}
You should then use prefetch_related to fetch all related jobs for all components in a single query. There is no need to query the jobs in your view
components = Component.objects.all().prefetch_related('jobs')
As the error describes, this code returns a TreeQuerySet object.
components = Component.objects.all()
Trying to call fields like price will result in the same error.
components = Component.objects.all()
Component_jobs = components.price.all()
The reason this code works is because it is called on a specific Component instead of a collection of Components.
component = Component.objects.all()
component_id = Component.objects.get(id=pk)
job_id = component_id.jobs.all()
To get all jobs on a queryset, try using something like .values('jobs') from the queryset api reference
all_jobs = Component.objects.all().values('jobs')
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 have basic models for a section, subsection and clause. 1 section can hold multiple subsections. Each subsection can hold multiple clauses. The models look like:
**models.py**
class Clause(models.Model):
number = models.CharField(max_length=8, unique=True)
requirements = models.TextField(max_length=2000, unique=False, blank=True, null=True)
documentation = models.TextField(max_length=2000, unique=False, blank=True, null=True)
class Subsection(models.Model):
number = models.CharField(max_length=5, unique=True)
name = models.CharField(max_length=150, unique=False)
descriptor = models.TextField(max_length=2000, unique=False, blank=True, null=True)
clause = models.ForeignKey(Clause, on_delete=models.DO_NOTHING, related_name="clause")
class Section(models.Model):
number = models.CharField(max_length=2, unique=True)
name = models.CharField(max_length=150, unique=False)
descriptor = models.TextField(max_length=2000, unique=False, blank=True, null=True)
subsection = models.ForeignKey(Subsection, on_delete=models.DO_NOTHING, related_name="subsection")
basic view function to call the desired section:
**views.py**
def main(request):
form = MainSearchForm()
user = request.user
sections = []
show_results = True
if 'query' in request.GET:
show_results = True
query = request.GET['query'].strip()
if len(query) <= 2:
sections = Section.objects.filter(number__iexact=query)
if sections:
records = sections
tpl = "display_console.html"
context = {'user': user, 'records': records, 'form': form}
return render(request, tpl, context)
else:
tpl = "main.html"
context = {'user': user, 'form': form}
return render(request, tpl, context)
unfortunately, I can't get my template to return my subsection data. The following returns a 'Subsection' object is not iterable error:
**template**
<table class="section_tb">
{% if records %}
{% for record in records %}
<tr>
<td>{{ record.number }}</td><td>{{ record.name }}</td>
</tr>
{% for item in record.subsection %}
<tr>
<td>{{ item.number }}</td><td>{{ item.name }}</td>
</tr>
<tr>
<td colspan=2>{{ item.descriptor }}</td>
</tr>
{% endfor %}
</table>
{% endfor %}
{% else %}
{% endif %}
substituting:
{% for item in record.subsection.all %}
for:
{% for item in record.subsection %}
removes the error message, but doesn't return any data. Is there something obvious I'm doing wrong?
This is because Section can have only one Subsection.
So you can access the subsection with just {{record.subsection}} so no forloop needed here.
As a tip remember when you usea one to many, the one is where the foreign key is defined.
The model that store the foreign key will always store only one foreign key.
If you want to access the many foreign keys from the other side use the model_name_in_lowercase_set or define a related name in models.ForeignKey(..., related_name="something") then you can call something_set