Reverse for Model with Slug in URL not working - django

I have a parts.html page that references the model Part, the page I am having issue with is the Part List Generic Model view. The purpose of it is to list all of the parts on a page and show the category next to it. Each part has a foreignkey "category". I am trying to create a Slug URL from the Part list view to the Category detail page, but I receive this error when I go to the partlistview page. I can access the Category page with portal/category/category where the second category at the end is a category in the database.
Note: The entire python files below are to scale, I only pasted in relevant information, as it would be too much info.
django.urls.exceptions.NoReverseMatch: Reverse for 'category-detail' with arguments '('',)' and keyword arguments '{}' not found. 1 pattern(s) tried: ['portal/category/(?P<slug>[-\\w]+)/$']
Views.py
class PartDetailView(LoginRequiredMixin, generic.DetailView):
model = Part
template_name = 'portal/part.html' # Option - Specify your own template name/location
class PartListView(LoginRequiredMixin, generic.ListView):
model = Part
#queryset = Part.objects.filter(is_active=True) #.prefetch_related('user') #hos
paginate_by = 100
https://simpleisbetterthancomplex.com/tutorial/2017/03/13/how-to-create-infinite-scroll-with-django.html
template_name = 'portal/parts.html' # Option - Specify your own template name/location
context_object_name = 'part_list'
#def get_queryset(self):
#return Category.objects.all()
def get_context_data(self, **kwargs):
context = super(PartListView, self).get_context_data(**kwargs)
context['category'] = Category.objects.all()
return context
class CategoryDetailView(LoginRequiredMixin, generic.DetailView):
model = Category
template_name = 'portal/category.html' # Option - Specify your own template name/location
slug_field = 'category'
slug_url_kwarg = 'category'
Models.py
#python_2_unicode_compatible
class Category(BaseModel):
"""
Model representing a part type (wiper, wheel, brake).
"""
id = models.BigAutoField(primary_key=True)
category = models.CharField(max_length=120, help_text=_("Category to organize parts into (wheel, wiperblade, brake)"))
slug = models.SlugField(editable=False, unique=True, db_index=True)
def __str__(self):
return '%s' % (self.category)
def get_absolute_url(self):
return reverse('portal:category-detail', kwargs={'slug': self.slug}) # str(self.trailer_number) kwargs={'slug': self.slug} instead of args=[self.slug]??
def save(self, *args, **kwargs):
self.slug = slugify(self.category)
super(Category, self).save(*args, **kwargs)
class Meta:
db_table = 'category'
verbose_name = _('category')
verbose_name_plural = _('categories')
ordering = ['category']
#python_2_unicode_compatible
class Part(BaseModel):
id = models.BigAutoField(primary_key=True)
name = models.CharField(max_length=120, help_text=_("The unique name or part number to identify this particular item"))
category = models.ForeignKey(Category, on_delete=models.PROTECT, help_text='Category to organize parts into (wheel, wiperblade, brake)', related_name='part', null=True, blank=True)
brand = models.CharField(max_length=80, blank=True, help_text=_("The name of the manufacturer that originaly built this part"), null=True)
minimum_inventory_quantity = models.PositiveSmallIntegerField(default=0, help_text=_("The minimum inventory quantity of this part before more need to be ordered"))
def get_absolute_url(self):
return reverse('portal:part-detail', args=[str(self.id)])
def __str__(self):
return '%s - %s' % (self.name, self.brand)
class Meta:
unique_together = ("name", "brand",) #NOTE: Each SKU + Carrier should also be unique together #unique_together = (("name", "manufacturer"), ("sku", "Carrier"))
ordering = ["name", "brand",]
Urls.py
from django.conf.urls import url
from .views import PartDetailView, CategoryDetailView, PartListView, PartInstanceDetailView, PartVariantDetailView
urlpatterns = [
url(r'^parts/$', PartListView.as_view(), name='parts'),
url(r'^parts/(?P<pk>\d+)/$', PartDetailView.as_view(), name='part-detail'),
url(r'^category/(?P<slug>[-\w]+)/$', CategoryDetailView.as_view(), name='category-detail'),
]
parts.html
<div class="table-responsive">
<table class="table table-hover table-bordered table-striped">
<thead>
<tr>
<th>Category</th>
<th>Name</th>
<th>Brand</th>
<th>Model Number</th>
<th>Variants</th>
<th>Stock</th>
<th>Locations</th>
</tr>
</thead>
<tbody>
{% for part in part_list %}
<tr>
<td><strong>{{ part.category.category }}</strong></td>
<td><a href="{% url 'portal:part-detail' part.pk %}">{{ part.name }}</td>
<td>{{ part.brand }}</td>
<td>{{ part.part_variant.model_number }}</td>
<td>{{ part.count_all }}</td>
<td>{{ part.count_all }}</td>
<td>{{ part.section.zone }}</td>
</tr>
{% empty %}
{% if request.GET.q %}
<tr>
<td colspan="7" class="text-center bg-warning">No trailers found.</td>
</tr>
{% else %}
<tr>
<td colspan="7" class="text-center bg-warning">There are no trailers in the system.</td>
</tr>
{% endif %}
{% endfor %}
</tbody>
</table>
</div>

I'm not entirely sure but I think your urls.py need an app_name = portal before the urls list.
Read up more in the docs.

Your url pattern is expecting a keyword argument, but the url template tag is passing the category slug as an unnamed argument.
Just do this {% url "portal:category-detail" slug=category.slug %},
because in your url pattern you named your capture group "slug".
Also, make sure category.slug is actually returning something.

Related

How to show name instad of id in django ForeignKey?

I have a model with a ForeignKey to itself.
models.py
class Person(models.Model):
name = models.CharField(max_length=20)
important_name = models.ForeignKey('Person', on_delete=models.SET_NULL, blank=True, null=True, related_name='first', verbose_name='Very Important Name')
def __str__(self):
return self.name
def get_fields(self):
return [(field.verbose_name, field.value_from_object(self)) for field in self.__class__._meta.fields]
And when i want to show my data with DetailView CBV in view it show id instead of name.
views.py
class DetailPerson(DetailView):
model = Person
template_name = 'panel/detail_person.html'
detail_person.html
<table class="table">
{% for label, value in object.get_fields %}
<tr>
<td>{{ label }}</td>
<td>{{ value }}</td>
</tr>
{% endfor %}
</table>
How can i show name instead of id?
Here we can use getattr instead of value_from_object, which will return us the related instance of Person instead of an ID.
def get_fields(self):
return [(field.verbose_name, getattr(self, field.name)) for field in self.__class__._meta.fields]
example:
from base.models import Person
a = Person.objects.first()
a.get_fields()
Returns
[('ID', 1), ('name', 'Joe'), ('Very Important Name', <Person: Bob>)]
Now, we may access all of Bob's attributes in the template as well, if the need arose.

Django : Dynamically update front end dropdown based on the end user's data in a product model

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

Display number of views for a specific post for specific user in a table using django

i want to display number of views in a table for a specific post. I already have the data stored in db. it seems that print statement print('count', context['count_view']) is working inside get_context_data method but it is not working as expected in the template. Don't worry about the data inside the image, its actually dummy data. Anyone helpenter image description here
models.py
class ObjectViewed(models.Model):
user = models.ForeignKey(User, blank=True, null=True, on_delete=models.CASCADE)
ip_address = models.CharField(max_length=220, blank=True, null=True)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) # User, Blog, or any other models
object_id = models.PositiveIntegerField() # User id, Blog id, or any other models id
content_object = GenericForeignKey('content_type', 'object_id')
timestamp = models.DateTimeField(auto_now_add=True)
views.py
class PostListView(ListView):
model = Post
template_name = 'edmin/post/postList.html'
context_object_name = 'posts'
ordering_by = ['-created']
def get_queryset(self):
post=Post.objects.filter(author=self.request.user)
return post
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
post=Post.objects.filter(author=self.request.user)
c_type = ContentType.objects.get_for_model(Post)
for p in post:
context['count_view'] = ObjectViewed.objects.filter(content_type=c_type, object_id=p.id).count()
print('count',context['count_view'])
return context
postList.html
{% for post in posts %}
{% if post.status == 'Draft' %}
{% else %}
<tr>
<th scope="row">{{ forloop.counter }}</th>
<td><a style="color:blue" href="{% url 'edmin:post_detail_view' pk=post.pk %}">{{ post.title }}</a></td>
<td>{{ post.banner_title }}</td>
<td>{{ post.created }}</td>
<td>{{ count_view }}</td>
<td>{{ post.status }}</td>
<td>Edit</td>
<td>Delete</td>
</tr>
{% endif %}
{% endfor %}
Since context allows 'dict', you can pass all of your views through context.

Class generic DeleteView not working for model related to model

I have a page containing assignment questions.
Questions related to assignment is displayed on asisgnment details page with EDIT and DELETE anchor tags.
But after pressing delete I get an error : Reverse for 'CreateQuestion' with arguments '('',)' not found. 1 pattern(s) tried: ['assignment/(?P[0-9]+)/createQuestion/$']
views.py
class AssignmentDelete(DeleteView):
model = Assignment
template_name = "dashboard/assignment_list.html"
success_url = reverse_lazy('ViewAssignment')
def get(self, request, *args, **kwargs):
return self.delete(request, *args, **kwargs)
class AssignmentDetailView(generic.DetailView):
model = Assignment
template_name = "dashboard/assignment_detail.html"
class QuestionDeleteView(DeleteView):
model = Question
template_name = 'dashboard/assignment_detail.html'
def get_success_url(self):
return reverse_lazy('assignment_detail', kwargs={'pk': self.object.assignment_id})
urls.py
path('<int:assignment_pk>/DeleteQuestion/<int:pk>/delete', views.QuestionDeleteView.as_view(), name='DeleteQuestion'),
path('<int:pk>/createQuestion/', views.QuestionCreate, name='CreateQuestion'),
path('assignment/<int:pk>', views.AssignmentDetailView.as_view(), name='assignment_detail'),
assignment_detail.html
{% extends "./base.html" %}
{% block content %}
<h1>Course:{{assignment.course }}</h1>
<p><strong>Assignment:</strong> {{ assignment.number }}</p>
<p><strong>publish_date:</strong> {{ assignment.publish_date }}</p>
<p><strong>deadline:</strong> {{assignment.deadline_date}}</p>
<div style="margin-left:20px;margin-top:20px">
<h4>Questions</h4>
<table class="table">
<thead class="thead-light">
<tr>
<th scope="col">Question Title</th>
<th scope="col">Question Marks</th>
<th scope="col">Edit Question</th>
<th scope="col">Delete Question</th>
</tr>
</thead>
<tbody>
{% for question in assignment.question_set.all %}
<tr>
<td>{{ question.title }}</td>
<td>{{ question.marks }}</td>
<td> Edit </td>
<td> <a onclick="return confirm('Are you sure?');" href="{% url 'DeleteQuestion' assignment_pk=assignment.id pk=question.id %}">Delete</a></td>
</tr>
{% endfor %}
</tbody>
</table>
<a class=" btn btn-primary" href="{% url 'CreateQuestion' assignment.id %}">Add Question</a>
</div>
{% endblock %}
{% block script %}
{% endblock %}
Models.py
class Assignment(models.Model):
number = models.IntegerField()
course = models.ForeignKey(Course,on_delete = models.SET_NULL,blank=True,null=True)
publish_date = models.DateField(auto_now_add = True)
deadline_date = models.DateField()
faculty = models.ForeignKey(Faculty,on_delete = models.SET_NULL,blank=True,null=True)
def __str__(self):
return f'Assignment {self.number}-{self.course}'
def get_absolute_url(self):
"""Returns the url to access a detail record for this Assignment."""
return reverse('assignment-detail', args=[str(self.id)])
class Question(models.Model):
assignment = models.ForeignKey(Assignment, on_delete=models.SET_NULL,blank=True,null=True)
username = models.ForeignKey(User,on_delete=models.SET_NULL,blank=True,null=True)
title = models.CharField(max_length = 200)
description = models.TextField(blank=True , null = True)
marks = models.IntegerField(null=True)
date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
def get_absolute_url(self):
"""Returns the url to access a detail record for this book."""
return reverse('question-detail', args=[str(self.id)])
The Assignment detail page with questions associated with it
I did some changes to my code and added a get method and now its working as I wanted it to.
It would be great if someone point out any risk related it.Thank you all for your time in helping in my problem
here is the UPDATED code :
class QuestionDeleteView(DeleteView):
model = Question
template_name = "dashboard/assignment_detail.html"
self.object.assignment_id})
def get(self, request, *args, **kwargs):
return self.delete(request, *args, **kwargs)
def get_success_url(self):
return reverse_lazy('assignment_detail', kwargs={'pk': self.object.assignment_id})

How to create a dynamic filter based on the primary key of ForeignKey in ListView?

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 %}