So I'm doing my first project using django showing examples of REST API methods like POST, GET, PUT, and DELETE through a made up database of first name, last name, and e-mail. I've been successful with POST and GET but now I am having trouble with PUT.
So I have three functions. First, a simple def function that shows all inputs of information so far. Second a class based function that lists the information in a specific order. And third, another class based function that shows the detail of that specific information. All functions work but now when I am linking the two html files together I am getting a error. I've tried a number of different ids when wanting to link to that specific information but they are just not working.
But here is my models.py:
class Information(models.Model):
"""Placeholder code that the viewer will be seeing."""
info_id = models.AutoField(primary_key=True,unique=True)
first_name = models.CharField(max_length=200)
last_name = models.CharField(max_length=200)
e_mail = models.CharField(max_length=200)
date_added = models.DateTimeField(auto_now_add=True)
def __str__(self):
"""Return information."""
return f"Info ID: {self.info_id}, First Name: {self.first_name}, Last Name: {self.last_name}, E-mail: {self.e_mail}"
Here is my forms.py:
class NameForm(forms.ModelForm):
class Meta:
model = Information
fields = ['info_id', 'first_name', 'last_name', 'e_mail']
labels = {'first_name': 'First Name:', 'last_name': 'Last Name:', 'e_mail': 'E-mail:'}
widgets = {'first_name': '',
'last_name': '',
'e_mail': '',
}
Here is my urls.py:
# Page(s) that shows the viewer an example of PUT.
path('PUT/', InfoDataListView.as_view(), name='put_data'),
path('PUT/<int:pk>/', InfoDataDetailView.as_view(), name='put_detail'),
Here is my views.py:
def put(request):
put = Information.objects.all()
context = {'put': put}
return render(request, 'just_projects/put.html', context)
class InfoDataListView(ListView):
model = Information
template_name = 'just_projects/put.html'
context_object_name = 'put'
ordering = ['-date_added']
class InfoDataDetailView(DetailView):
model = Information
Here are my two htmls: put.html and information_detail.html
{% extends "just_projects/base.html" %}
{% block content %}
<p><h3>Information:</h3></p>
<ul>
{% for pt in put %}
<div class="card mb-3">
<h4 class="card-header">
{{ pt.date_added|date:'M d, Y H:i' }}
<small>Edit info.</small>
</h4>
<div class="card-body">
{{ pt }}
</div>
</div>
{% empty %}
<li>There is no information yet.</li>
{% endfor %}
</ul>
<hr />
{% endblock content %}
{% extends "just_projects/base.html" %}
{% block content %}
<p><h3>Information:</h3></p>
<ul>
<div class="card mb-3">
<h4 class="card-header">
{{ object.date_added|date:'M d, Y H:i' }}
</h4>
<div class="card-body">
{{ object }}
</div>
</div>
</ul>
<hr />
<p>
</p>
{% endblock content %}
So the error message is a reversematch because they can't find the correct id right? The specific exception error and value is:
Exception Type: NoReverseMatch
Exception Value:
Reverse for 'put_detail' with arguments '('',)' not found. 1 pattern(s) tried: ['PUT/(?P[0-9]+)/$']
EDIT: Thank you Daniel Roseman!!!
You are using the wrong variable name within your url tag. Your object is called pd, so that is where the ID needs to come from.
<a href="{% url 'just_projects:put_detail' pd.id %}">
(Unrelated, but your clean method is pointless; it is within the Meta class so it will never actually be called, but also it does nothing useful. You should delete it.)
Edit looking at your models, your primary key is called info_id for some reason. So either do pd.info_id, or just pd.pk.
Although I would question why you need to change the name of the primary key in the first place.
Related
I am trying to implement Listing Filter from django filters. First "type" is the attribute that I want my filter to be based inside models.py of my app.
class detaileditems(models.Model):
title = models.CharField(max_length= 255)
type = models.CharField(max_length= 45, null=True)
pubdate = models.DateTimeField()
body = models.TextField()
image = models.ImageField(upload_to= 'images/')
I have created a separate filters.py inside my application where I have called the filters.
import django_filters
from .models import detaileditems
class ListingFilters(django_filters.FilterSet):
class Meta:
model = detaileditems
fields = {'type': ['exact']}
Next here is my function inside views.py file-
from .models import detaileditems
from .filters import ListingFilters
def alldetailed2(request):
items = detaileditems.objects
listing_filter = ListingFilters(request.GET, queryset=items)
context = {
'listing_filter' : listing_filter,
'items': items,
}
return render(request, 'detailed2/detailed2.html',context)
Lastly in my html file "detailed2.html" which is inside the application template folder of "detailed2".
<div class = "col-lg-6 col-md-8 mx-auto">
<form method = "get">
{{ listing_filter.form }}
<button class="btn btn-sm btn-danger" type="submit">Search</button>
</form>
</div>
<div class = "container">
<div class = "row row-cols-1 row-cols-sm2 row-cols-md-3 g-3">
{% for listing in listing_filter.qs %}
<div class = "col">
{% include "detailed2/detailed2.html" %}
</div>
{% endfor %}
</div>
</div>
I am getting a maximum recursion depth error.
And here is my folder structure for better understanding.
There is an earlier post on maximum recursion depth:
earlier post
Sometimes a solution can be found by rethinking the algorithm so it uses iteration over for example a list instead of recursion. I guess you need to convince yourself you are not making an error and then file an issue with Django.
As a quick fix you can try to increase the recursion depth. See post I linked to.
I do see a self reference in the html:
'''
{% include "detailed2/detailed2.html" %}
'''
could that be the root cause of your trouble?
i guess you missed form action. When there is a keyword entered then form submission should call a same function as you're using for listing all the articles.
<form method = "post" action="{% url 'alldetailed2' %}">
<input type='text' name='articale_name' />
<button class="btn btn-sm btn-danger" type="submit">Search</button>
</form>
in View function:
article_name = request.POST.get("articale_name")
if article_name:
"filter model with article_name"
else:
list out all the Articles same as earlier
In OP's views.py, ensure it has .all() at the end when OP is getting the items
def alldetailed2(request):
items = detaileditems.objects.all()
listing_filter = ListingFilters(request.GET, queryset=items)
context = {
'listing_filter' : listing_filter,
'items': items,
}
return render(request, 'detailed2/detailed2.html',context)
Then one can render in one's template like this
{% for detailed_item in listing_filter.qs %}
<h2>{{ detailed_item.title }}</h2>
<p>{{ detailed_item.body }}</p>
{% endfor %}
instead of the
{% include "detailed2/detailed2.html" %}
Keep in mind as well that there are guidelines in the way one names classes, functions, ....
Django models should use the CapWords convention. Also, it's considered and anti-pattern to use model classes with a plural name. So, DetailedItem instead of detaileditems.
The functions names should be lowercase, with words separated by underscores, like all_detailed_2 instead of alldetailed2.
I am creating a blog application in django where i encountered this unusual issue. I am not being able to filter and display blog posts category wise. Please find my code below. Thanks in advance. I have spent two full days trying to figure this out and still
got nothing.
MODELS.py
This is the model which i have created for a blog post.
class Post(models.Model):
title = models.CharField(max_length=255)
author = models.ForeignKey(User, on_delete=models.CASCADE)
body = models.TextField()
post_date = models.DateField(auto_now_add=True)
category = models.CharField(max_length=255, default="uncategorized")
def __str__(self):
return self.title + ' | ' + str(self.author)
def get_absolute_url(self):
#return reverse('article-detail', args=(str(self.id)))
return reverse('homepage')
VIEWS.py
This is the view that i have created for a category wise blog posts.
def CategoryView(request,cat):
category_posts = Post.objects.filter(category=cat)
return render(request,'categories.html',{'category_posts':category_posts})
URLS.py
The urls.py file.
path('category/<str:cat>/', CategoryView, name = 'category'),
CATEGORIES.html
This will be the final display page where the category_posts is displayed as an empty queryset. The for loop is unable to run because category_posts is an empty queryset. Single word categories are also empty(to rule out a slugify issue)
{% extends 'base.html' %}
{% load static %}
{% block content %}
<ul>
{% for post in category_posts %}
<li>
<div class="category">
{{ post.category }}
</div>
<a href="{% url 'article-detail' post.pk %}">
<h3><b>{{ post.title }} </b></h3>
<p class=card-date>
<big>{{ post.author }}</big>
<small>-{{ post.post_date }}</small>
</p>
<p class="excerpt-text">{{ post.body | slice:"100" |safe}}</p>
<div class="article-thumbnail">
<img src="{% static "images/bg.png" %}" alt="article-thumbnail" >
</div>
<div class="overlay">
</a>
</li>
{% endfor %}
{% endblock %}
I'm trying to show a model's ForeignKey value in a template, all other fields are showing fine but I couldn't make it work. Here is my code:
models.py:
class BodyPart(models.Model):
body_part = models.CharField(max_length=20, unique = True)
class Exercise(models.Model):
body_part = models.ForeignKey(BodyPart, on_delete=models.CASCADE, default = "bodypart", related_name="bodypart")
views.py:
exercises = Exercise.objects.filter(category=exercisedetailcategory).values()
context = {
"exercises" : exercises,
}
return render(request,"exercises-categories.html",context)
template:
{% for exercise in exercises %}
<span class="post-meta-category">{{exercise.body_part}}</span>
<div class="post-item-description">
{{exercise.title}}
<p>{{exercise.content}}</p>
{% endfor %}
This is one of the many reasons why you should not use .values(). If you pass Exercise models, you can fetch the related object into memory. You can make use of .select_related(..) to optimize the query:
exercises = Exercise.objects.filter(
category=exercisedetailcategory
).select_related('body_part')
context = {
'exercises' : exercises,
}
return render(request, 'exercises-categories.html', context)
Then in the template, we can render this with:
{% for exercise in exercises %}
<span class="post-meta-category">{{ exercise.body_part.body_part }}</span>
<div class="post-item-description">
{{ exercise.title }}
<p>{{ exercise.content }}</p>
{% endfor %}
You can furthermore implement a __str__ method for BodyPart:
class BodyPart(models.Model):
body_part = models.CharField(max_length=20, unique=True)
def __str__(self):
return self.body_part
and then render this with:
{% for exercise in exercises %}
<span class="post-meta-category">{{ exercise.body_part }}</span>
<div class="post-item-description">
{{ exercise.title }}
<p>{{ exercise.content }}</p>
{% endfor %}
in your exercise model, ignore the default part.(its possible to show any message that tell users "no body_part" such as
{% if not exercise.body_part %} -> No thing to show)
and make sure, you have a value in your exercise.body_part Which it means you have to have an object in your BodyPart model in relation to the current object of this model.
also it should be {{ exercise.body_part.body_part }}
the second one is to extract the value of related BodyPart objects value
I am trying to access dict values in my django template. The dict keys are objects. Here is a snippet of my codebase:
models.py
class ProductCategory(models.Mode):
name = models.CharField(max_length=64, blank=False, null=False, unique=True)
slug = models.SlugField(blank=False, null=False, unique=True)
class Product(models.Model):
category = models.ForeignKey(ProductCategory, on_delete = models.CASCADE)
# other fields
pass
def __str__(self):
return self.title
views.py
def index(request):
products = Product.objects.all()
categorized_products = {}
for p in products:
prod_category = p.category
temp = categorized_products.get(prod_category,[])
temp.append(p)
categorized_products[prod_category] = temp
context = {'products_dict': categorized_products, 'categories': categorized_products.keys() }
return render(request,'product/index.html', context=context)
mytemplate.html (relevant snippet)
<div class="tab-content">
{% for category in categories %}
{% if not forloop.counter0 %}
<div class="tab-pane fade in active" id="{{ category.slug }}">
{% else %}
<div class="tab-pane fade in" id="{{ category.slug }}">
{% endif %}
<div >
<h4>{{ category.description }}</h4>
<div class="container-fluid">
<div class="row">
<div class="col-md-3" style="border:1px solid red;">
{% for product in products_dict.category %}
{{ product }}
{% endfor %}
When I step through the code with a debugger, I can see that the variable products_dict is a dict and correctly populated by the view. However, when I run the code, the for loop code is not executed.
Similarly, when I run the same code I have in views.py in the shell, the dict is correctly populated, so I know that there is data in the database and that the retrieved data is being correctly passed to the django template.
So why am I unable to access the dict value in the template and why is the product not displaying in my template?
The simple answer is that Django templates don't do that. Dot reference in template variables does dictionary lookup (among other things) but not based on the value of another variable name:
Note that “bar” in a template expression like {{ foo.bar }} will be interpreted as a literal string and not using the value of the variable “bar”, if one exists in the template context.
https://docs.djangoproject.com/en/3.0/ref/templates/language/#variables
Generally I would solve this by arranging for something iterable to exist on the category values directly, rather than trying to use the category object as a dict key. That could be a reverse FK manager:
{% for product in category.products_set.all %}
Or something I set on the in-memory category objects in the view, if I need to transform or filter the products per-category:
categories = ProductCategory.objects.all()
for category in categories:
category.products = [transform_product_somehow(product) for product in category.products_set.all()]
(Optionally with use of fetch_related to prefetch the category products rather than doing an additional query per category.)
You can use a custom filter for that. In custom_tags.py:
from django import template
register = template.Library()
#register.filter
def get_obj_field(obj, key):
return obj[key]
Then load the tags in django:
{% load custom_tags %}
And use it like this:
{% for product in products_dict|get_obj_field:category %}
{{ product }}
{% endfor %}
In my app, I have 3 models: Issue, Series, and Character. The Issue model has a Series ForeignKey, and a Character ManyToManyField. Here they are simplified:
class Character(models.Model):
name = models.CharField('Character name', max_length=200)
desc = models.TextField('Description', max_length=500)
class Series(models.Model):
name = models.CharField('Series name', max_length=200)
desc = models.TextField('Description', max_length=500)
class Issue(models.Model):
series = models.ForeignKey(Series, on_delete=models.CASCADE, blank=True)
name = models.CharField('Issue name', max_length=200)
number = models.PositiveSmallIntegerField('Issue number')
date = models.DateField('Cover date')
desc = models.TextField('Description', max_length=500)
characters = models.ManyToManyField(Character, blank=True)
cover = models.FilePathField('Cover file path', path="media/images/covers")
I have a Character template that displays information about the character. I want to also display Issues the Character is in, sorted by Series.
{% extends "app/base.html" %}
{% block page-title %}{{ character.name }}{% endblock page-title %}
{% block content %}
<div class="description">
<p>{{ character.desc }}</p>
</div>
<div class="issues">
<h3>Issues</h3>
{% for series in character.issue_set.all %}
<div>
{{ series.name }}
<ul>
{% for issue in character.issue_set.all %}
{% if issue.series.name == series.name %}
<li>
<img src="/{{ issue.cover }}" alt = "{{ series.name }}" >
<p>Issue #{{ issue.number }}</p>
</li>
{% endif %}
{% endfor %}
</ul>
</div>
{% endfor %}
</div>
{% endblock content %}
Obviously, the way this currently formats is that for every issue in the set, it outputs the series title, and then each issue in the set.
<div class="issues">
<h3>Issues</h3>
<div>
Series 1
<ul>
<li>
<img src="/media/images/covers/01.jpg" alt="Series 1">
<p>Issue #1</p>
</li>
<li>
<img src="/media/images/covers/02.jpg" alt="Series 1">
<p>Issue #2</p>
</li>
</ul>
</div>
<div>
Series 1
<ul>
<li>
<img src="/media/images/covers/01.jpg" alt="Series 1">
<p>Issue #1</p>
</li>
<li>
<img src="/media/images/covers/02.jpg" alt="Series 1">
<p>Issue #2</p>
</li>
</ul>
</div>
</div>
Here's what I would like to see:
<div class="issues">
<h3>Issues</h3>
<div>
Series 1
<ul>
<li>
<img src="/media/images/covers/01.jpg" alt="Series 1">
<p>Issue #1</p>
</li>
<li>
<img src="/media/images/covers/02.jpg" alt="Series 1">
<p>Issue #2</p>
</li>
</ul>
</div>
</div>
I've researched quite a bit on templating, and I'm not seeing a way to get a listing based on distinct values. I've also tried creating a new set in my Character or Issue model that I could use to replace issue_set.all, but I have yet to get it working.
EDIT: Upon request of marcusshep, the Character view is using the generic DetailView:
class CharacterView(generic.DetailView):
model = Character
template_name = 'app/character.html'
I would use a function based view rather than a class based generic view. Reason being that your required behavior is going beyond something generic.
In your function you can build the queryset you desire instead of having to fight with the one provided by generic.DetailView.
def my_view(request, *args, **kwargs):
character = Character.objects.get(id=request.GET.get("id", None))
issues = character.issue_set.all().order_by("series__name")
return render(request, 'app/character.html', {"issues": issues})
Alternatively, you can use what you already have and override the DetailView's get_queryset() method.
class CharacterView(generic.DetailView):
model = Character
template_name = 'app/character.html'
def get_queryset():
# return correct queryset
The biggest problem though is that there will be more aspects that will need to use this set. For instance, I'll be adding Creators, Story Arcs, etc. they will have their own pages and will need to display related issues, sorted by series as well. It would be nice to have a solution that can be used by any of these templates without much code re-use.
This is a very common problem in all areas of programming. A very simple way to solve this would be to isolate the logic in one function and call that function whenever you need it.
def my_issues_query():
# find the objects you need
def my_view(request, *args, **kwargs):
issues = my_issues_query()
You can also take advantage of pythons decorator functions. (Which is my favorite approach.)
def has_issues(view_function):
def get_issues(request, *args, **kwargs):
# find all the issues you need here
# you'll only need to write this logic once.
issues = Issues.objects.filter(...)
return issues
return get_issues
#has_issues
def my_view(request, *args, **kwargs):
# this functions namespace now contains
# the variable `issues`.
# which allows for the use of the query ie.
return render(
request,
"my_templates/template.html",
{"issues":issue}
)