What I'm looking to do is make it to where I can search the database directly without having the entire database visible on search page. The search page should be empty. You do a search and it goes straight to the detail page of the db entry which matches your search criteria. If there is no match it shows "No matches found". How can I do this?
This is what I have so far. This prints/lists every entry in the database on the same page as my search box and when a search is done the list gets cut down to however many entries match the search criteria. Every db entry has a link you can click on which will take you to a detail page which pulls up that entry's info.
I'm using Django 2.0.
Manage.py
class PostManager(models.Manager):
def active(self, *args, **kwargs):
return super(PostManager, self).filter(draft=False).filter(publish__lte=timezone.now())
def upload_location(instance, filename):
return "%s/%s" %(instance.slug, filename)
class Remote(models.Model):
brand = models.CharField(max_length=20)
model = models.CharField(max_length=25)
type = models.CharField(max_length=50)
orig = models.TextField()
comp = models.TextField(blank=True)
image = models.ImageField(upload_to=upload_location,
null=True, blank=True, default=0,
timestamp = models.DateTimeField(auto_now=False, auto_now_add=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE, default=1)
slug = models.SlugField(unique=True)
objects = PostManager()
def __str__(self):
return self.model
def get_absolute_url(self):
return reverse("mainapp:detail", kwargs={"slug": self.slug})
def create_slug(instance, new_slug=None):
slug = slugify(instance.model) #slugify title
if new_slug is not None:
slug = new_slug
qs = Remote.objects.filter(slug=slug).order_by("-id")
exists = qs.exists()
if exists:
new_slug = "%s-%s" %(slug, qs.first().id)
return create_slug(instance, new_slug=new_slug)
return slug
def pre_save_post_receiver(sender, instance, *args, **kwargs):
if not instance.slug:
instance.slug = create_slug(instance)
pre_save.connect(pre_save_post_receiver, sender=Remote)
Views.py
def detail(request, slug=None):
instance = get_object_or_404(Remote, slug=slug)
share_string = quote_plus(instance.orig)
context={
"model": instance.model,
"instance": instance,
"share_string": share_string,
}
return render(request, 'mainapp/remote_detail.html', context)
def remote_list(request):
today = timezone.now().date()
queryset_posts = Remote.objects.all()
if request.user.is_staff or request_user_is_superuser:
queryset_posts = Remote.objects.all()
query = request.GET.get("q") # search stuff
if query:
queryset_posts = queryset_posts.filter(
Q(model__icontains=query)|
Q(orig__icontains=query)
).distinct() #.distinct() doesn't allow duplicates to show up
paginator = Paginator(queryset_posts,10) # Show 25 contacts per page
page_request_var = "pagina" #this changes "Page name in pagination
page = request.GET.get(page_request_var)
queryset = paginator.get_page(page)
context={
"object_list": queryset,
"title": "List",
"page_request_var": page_request_var,
"today": today,
}
return render(request, 'mainapp/remote_list.html', context)
HTML for Remote_list view
<form class='mb-2' method='GET' action=''>
<input type='text' name='q' placeholder='Search posts' value='{{ request.GET.q }}'/>
<input href='{{ obj.get_absolute_url }}' type='submit' value='Search'/>
</form>
{% for obj in object_list %}
{% if obj.image %}
<img class="card-img-top" src="{{ obj.image.url }}" alt="Image" style="max-width:100px;">
{% endif %}
<h5 class="card-title"><a href='{{ obj.get_absolute_url }}'>{{ obj.model }}</a></h5>
{% if obj.user.get_full_name %}
<p>Author: {{ obj.user.get_full_name }}</p>
{% endif %}
<p class="card-text">{{ obj.orig|truncatechars:120 }}</p>
View
{% endfor %}
HTML for Detail view
{% if instance.image %}
<img src='{{ instance.image.url }}' class='img-responsive' style='max-width:300px;' />
{% endif %}
<h3>{{ instance.brand}} {{ model }}</h3>
{% if instance.user.get_full_name %}
<p>Author: {{ instance.user.get_full_name }}</p>
{% endif %}
<h5>Originally Supplied With: </h5> {{ instance.orig }}
{% if instance.comp %}
<h5>Compatible With: </h5>{{ instance.comp }}
{% endif %}
you need to add some codes to your qs but now it's okey
you need to learn more about filtering query and passing them to your views if your models contains title field you can do {{ your_data.title }} in your template with your_data is the filtered passed query
Related
I have to do wishlist, I have done wishlist page, model and html.bBut when I click on the button bellow my post, I'm redirected to wishlist page and post didnt saved in my wishlist.So thats my code:
models.py
class Wishlist(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
wished_item = models.ForeignKey(Posts, on_delete=models.CASCADE)
def __str__(self):
return self.wished_item.title
class Posts(models.Model):
TYPE = Choices(
('private', _('private')),
('business', _('business')),
)
STATUS = Choices(
('active', _('active')),
('deactivated', _('deactivated'))
)
owner = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, related_name='posts',
on_delete=models.CASCADE, verbose_name='owner')
phone_number = PhoneNumberField(verbose_name=_('Phone_number'), null=False, blank=False, unique=True)
title = models.CharField(verbose_name=_('Title'), max_length=100)
text = RichTextField(verbose_name=_('Text'))
image = models.ImageField(upload_to='images/%Y/%m/%d/', null=True, blank=True, validators=[file_size])
price = models.DecimalField(verbose_name=_('Price'), decimal_places=2, max_digits=9)
status = models.CharField(choices=STATUS, max_length=50)
created = models.DateTimeField(auto_now=True)
type = models.CharField(choices=TYPE, max_length=50)
def __str__(self):
return self.title
views.py
class WishListView(generic.View):
def get(self, *args, **kwargs):
wish_items = Wishlist.objects.filter(user=self.request.user)
context = {
'wish_items': wish_items
}
return render(self.request, 'wishlist/wishlist.html', context=context)
def addToWishList(request):
if request.method == 'POST':
post_var_id = request.POST.get('object-id')
post_var = Posts.objects.get(id=post_var_id)
print(post_var)
try:
wish_item = Wishlist.objects.get(user=request.user, post=post_var)
if wish_item:
wish_item.save()
except:
Wishlist.objects.create(user=request.user, post=post_var)
finally:
return HttpResponseRedirect(reverse('wishlist'))
wishlist.html
{% extends 'posts/base.html' %}
{% load thumbnail %}
{% block content %}
<div>
{% for item in wish_items %}
{% if item.wished_item.image1 %}
<img src="{{item.wished_item.image.url}}" alt="">
{% endif %}
</div>
<div>
<li>{{item.wished_item.title}}</li>
<li>{{item.wished_item.text}}</li>
<li>{{item.wished_item.price}}</li>
<li>{{item.wished_item.phone_number}}</li>
{% if item.wished_item.image %}
<img src="{% thumbnail item.wished_item.image 200x200 crop %}" alt="" />
<p></p>
{% endif %}
</div>
{% endfor %}
{% endblock %}
urls.py
urlpatterns = [
path("wishlist/", WishListView.as_view(), name='wishlist'),
path("add-to-wishlist", addToWishList, name='add-to-wishlist'),
]
and all posts template with add to wishlist button.
<ul>
{% for object in object_list %}
<li>Owner: {{ object.owner }}</li>
<li>Phone: {{ object.phone_number }}</li>
<li>Title: {{ object.title }}</li>
<li>Text: {{ object.text }}</li>
<li>Type: {{ object.type }}</li>
<li>Price: {{ object.price }}</li>
<li>Date: {{ object.created }}</li>
<p>
{% if object.image %}
<img src="{% thumbnail object.image 200x200 crop %}" alt="" />
{% endif %}
</p>
<form action="{% url 'add-to-wishlist' %}" method="POST">
{%csrf_token%}
<input type="hidden" name="object-id" value="{{object.id}}">
<input type="submit" value="Add to Wishlist">
</form>
<hr/>
Probably problem with posts Id, but I'm not sure in that.
You can use get_or_create instead of the if else statement for if exist or not. And use get_object_or_404 to make code more clear.
from django.shortcuts import get_object_or_404
def addToWishList(request):
if request.method == 'POST':
post_obj = get_object_or_404(Post, pk=request.POST.get('object-id'))
Wishlist.objects.get_or_create(user=request.user, post=post_obj)
return HttpResponseRedirect(reverse('wishlist'))
In your views.py try to replace these lines:
try:
wish_item = Wishlist.objects.get(user=request.user, post=post_var)
if wish_item:
wish_item.save()
except:
Wishlist.objects.create(user=request.user, post=post_var)
with
wish_item, was_created = Wishlist.objects.get_or_create(user=request.user, post=post_var)
# for debugging
if was_created:
print(f"{wish_item} was created")
else:
print(f"{wish_item} already exists")
what is the output?
The code looks OK for me. You might add a trailing / in your urls.py after the path("add-to-wishlist/" ... but I can't spot anything wrong in the first place.
I am learning Python and Django, and I am trying to create a recipe with a form. The problem is I have two models, Recipe and RecipeIngredient and I don't know how to add recipe ingredients to the form since it requires the primary key of the recipe and from what I understand, the key is not created until after the form is saved? So how can I create a recipe with both the Recipe and RecipeIngredient when Recipe is not yet initialized?
Models.py:
class Recipe(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
image = models.ImageField(upload_to='image/', blank=True, null=True)
name = models.CharField(max_length=220) # Lasanga
description = models.TextField(blank=True, null=True)
notes = models.TextField(blank=True, null=True)
cookTime = models.CharField(max_length=50, blank=True, null=True)
timeStamp = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
active = models.BooleanField(default=True)
#property
def title(self):
return self.name
def get_absolute_url(self):
return reverse("recipes:detail", kwargs={"id": self.id}) # recipes is from url.py app_name
def get_hx_url(self):
return reverse("recipes:hx-detail", kwargs={"id": self.id}) # recipes is from url.py app_name
def get_edit_url(self):
return reverse("recipes:update", kwargs={"id": self.id})
def get_image_upload_url(self):
return reverse("recipes:recipe-ingredient-image-upload", kwargs={"parent_id": self.id})
def get_delete_url(self):
return reverse("recipes:delete", kwargs={"id": self.id})
def get_ingredients_children(self):
return self.recipeingredient_set.all()
def get_instruction_children(self):
return self.recipeinstruction_set.all()
class RecipeIngredient(models.Model):
recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE)
name = models.CharField(max_length=220) # grilled chicken pasta
description = models.TextField(blank=True, null=True)
quantity = models.CharField(max_length=50, blank=True, null=True)
unit = models.CharField(max_length=50, validators=[validate_unit_of_measure], blank=True, null=True)
instructions = models.TextField(blank=True, null=True)
timeStamp = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
active = models.BooleanField(default=True)
def get_absolute_url(self):
return self.recipe.get_absolute_url() # recipe cannot be none
def get_hx_edit_url(self):
kwargs = {
"parent_id": self.recipe.id,
"id": self.id
}
return reverse("recipes:hx-ingredient-detail", kwargs=kwargs)
def get_delete_url(self):
kwargs = {
"parent_id": self.recipe.id,
"id": self.id
}
return reverse("recipes:ingredient-delete", kwargs=kwargs)
Views.py
#login_required
def recipe_create_view(request):
form = RecipeForm(request.POST or None)
context = {
"form": form,
}
if form.is_valid():
obj = form.save(commit=False)
obj.user = request.user
obj.save()
if request.htmx: # necessary to pass headers from htmx response if we want the django to recognise the htmx change
headers = {
"HX-Redirect": obj.get_absolute_url()
}
return HttpResponse("Created", headers=headers)
# if request.htmx: # could use this but the url doesn't update, stays as create, would need to use HX-Push header & HttpResponse + context somehow
# context = {
# "object": obj
# }
# return render(request, "recipes/partials/detail.html", context)
return redirect(obj.get_absolute_url())
return render(request, "recipes/create.html", context)
#login_required
def recipe_ingredient_update_hx_view(request, parent_id=None, id=None): # this is both create & edit, can create
if not request.htmx:
raise Http404
try:
parent_obj = Recipe.objects.get(id=parent_id, user=request.user)
except:
parent_obj = None
if parent_obj is None:
return HttpResponse("Not Found.")
instance = None
if id is not None:
try:
instance = RecipeIngredient.objects.get(recipe=parent_obj, id=id) # think of this as an object if that helps
except:
instance = None
form = RecipeIngredientForm(request.POST or None, instance=instance)
url = reverse("recipes:hx-ingredient-create", kwargs={"parent_id": parent_obj.id})
if instance:
url = instance.get_hx_edit_url()
context = {
"url": url,
"form": form,
"object": instance
}
if form.is_valid():
new_obj = form.save(commit=False)
if instance is None:
new_obj.recipe = parent_obj
new_obj.save()
context['object'] = new_obj # because it's possible the object/instance in None
return render(request, "recipes/partials/ingredient-inline.html", context)
return render(request, "recipes/partials/ingredient-form.html", context)
forms.py
from django import forms
from .models import Recipe, RecipeIngredient
class RecipeForm(forms.ModelForm):
class Meta:
model = Recipe
fields = ['name', 'image', 'description', 'notes', 'cookTime']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field in self.fields:
new_data = {
"placeholder": f"Recipe {str(field)}",
"class": "form-control",
}
self.fields[str(field)].widget.attrs.update(new_data)
class RecipeIngredientForm(forms.ModelForm):
class Meta:
model = RecipeIngredient
fields = ['name', 'quantity', 'unit']
urls.py
from django.urls import path
from .views import (
recipe_list_view,
recipe_delete_view,
recipe_create_view,
recipe_update_view,
recipe_detail_hx_view,
recipe_ingredient_update_hx_view,
recipe_ingredient_delete_view,
recipe_ingredient_image_upload_view,
recipe_ingredient_url_scrape_view
)
app_name='recipes' # allows use of recipes:list as a reverse url call
urlpatterns = [
path('', recipe_list_view, name='list'), # index / home / root
path('create/>', recipe_create_view, name='create'),
path('hx/<int:parent_id>/ingredient/<int:id>', recipe_ingredient_update_hx_view, name='hx-ingredient-detail'), #or detail
path('hx/<int:parent_id>/ingredient/', recipe_ingredient_update_hx_view, name='hx-ingredient-create'),
]
create.html
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% load static %}
{% block content %}
<div class="container-fluid px-5">
<h1 class="pb-5">Create Recipe</h1>
<div id="recipe-container">
<form action='.' method="POST" hx-post='.'>
{% csrf_token %}
<div class='row'>
<div class="row d-flex pb-5">
<div class="col-12 col-lg-6 justify-content-center d-flex order-first order-lg-last pictureBox"
style="height: 400px; width:450; border: solid tomato 1px;">
<div class="align-self-center">
{{ form.image|as_crispy_field }}
</div>
</div>
<div class="col-12 col-lg-6 order-lg-first">
<div class="pb-3">{{ form.name|as_crispy_field }}</div>
<div class="pb-3">{{ form.description|as_crispy_field }}</div>
</div>
</div>
<div class="col-12 col-md-6">
{{ form.notes|as_crispy_field }}
</div>
<div class="col-12 col-md-6">
{{ form.cookTime |as_crispy_field }}
</div>
</div>
</div>
<div class='col-12 col-md-4'>
<!-- ADD INGREDIENTS ? -->
</div>
<div class="htmx-indicator">Loading...</div>
<div class="d-flex">
<button class="btn btn-success htmx-inverted-indicator" style='margin-top:10px;' type='submit'>Save</button>
<a class="btn btn-danger" href='{% url "recipes:list" %}'>Delete</a>
</div>
{% if message %}
<p>{{ message }}</p>
{% endif %}
</form>
</div>
</div>
{% endblock content %}
ingredient-form.html
<form action='.' method="POST" hx-post='{% if url %} {{ url }} {% else %} . {{% endif %}' hx-swap='outerHTML'>
{% csrf_token %}
{{ form.as_p}}
<div class="htmx-indicator">Loading...</div>
<button class="htmx-inverted-indicator" style='margin-top:10px;' type='submit' >Save</button>
</form>
ingredient-inline.html
<div class="py-1" id="ingredient-{{object.id}}">
<p>{% if not edit %} <b>{{ object.quantity }} {% if object.unit %} {{ object.unit }} {% endif %}</b> {% else %} {{ object.quantity }} {{ object.unit }} {% endif %} - {{ object.name }}</p>
{% if edit %}
<button class="btn btn-primary" hx-trigger='click' hx-get='{{ object.get_hx_edit_url }}' hx-target="#ingredient-{{object.id}}">Edit</button> <!-- target will replace whole div-->
<button class="btn btn-danger" href='{{ object.get_delete_url }}' hx-post='{{ object.get_delete_url }}' hx-confirm="Are you sure you want to delete {{ object.name }}?" hx-trigger='click' hx-target="#ingredient-{{object.id}}" hx-swap="outerHTML">Delete</button>
{% endif %}
</div>
The key to this problem is using a formset, as you will likely want to save multiple ingredients to the recipe. Django documentation outlines how to use them. Your view would end up looking something like below, allowing you to save the parent model, which will give you the parent instance/primary key to then save the ingredients.
def recipe_create_view(request):
form = RecipeForm(request.POST or None)
RecipeIngredientFormset = formset_factory(RecipeIngredientForm)
formset = RecipeIngredientFormset(request.POST or None)
context = {
"form": form,
"formset": formset,
}
if request.method == "POST":
if form.is_valid() and formset.is_valid():
parent = form.save(commit=False)
parent.user = request.user
parent.save()
#recipe ingredients
for form in formset:
child = form.save(commit=False)
child.recipe = parent
child.save()
how can I retrieve the lesson modules by its parent lesson strictly? e.g if I change to the next lesson where at the same contains a lessonmodule /lesson1/task1 this one shouldn't display since it doesn't belong to lesson2? how can I fix this by only retrieve slugs, content by the lesson attach to?
views
class LessonDetailView(LoginRequiredMixin, View):
login_url = "/account/login/"
def get(self, request, course_slug, lesson_slug, *args, **kwargs):
lesson = get_object_or_404(Lesson.objects.select_related('course'), slug=lesson_slug, course__slug=course_slug)
lessonmodule = get_object_or_404(LessonModule.objects.select_related('lesson'), slug='hello_world')
context = {
'object': lessonmodule,
'previous_lesson': lesson.get_previous_by_created_at,
'next_lesson': lesson.get_next_by_created_at,
}
return render(request, "courses/lesson_detail.html", context)
models
class Lesson(models.Model):
title = models.CharField(max_length=120)
slug = models.SlugField(max_length=25,unique=True)
course = models.ForeignKey(Course, on_delete=models.CASCADE)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('lesson-detail',
kwargs={
'course_slug': self.course.slug,
'lesson_slug': self.slug
})
class LessonModule(models.Model):
slug = models.SlugField(unique=True, blank=True, null=True)
content = models.TextField()
lesson = models.ForeignKey(Lesson, on_delete=models.CASCADE)
allowed_memberships = models.ManyToManyField(Membership)
created_at = models.DateTimeField(auto_now_add=True, blank=True)
updated_at = models.DateTimeField(auto_now=True, blank=True)
def __str__(self):
return self.slug
template
{% block content %}
<div class='container'>
<h1>Lesson Detail View</h1>
<div class='row'>
<div class='col-sm-6 col-md-6'>
{% if object is not None %}
<h3>{{ object.title }}</h3>
<p>{{ object.course.description }}</p>
{{object}}
{% if previous_lesson %}
Back
{% endif %}
{% if next_lesson %}
Next
{% endif %}
{% else %}
<h3>Upgrade membership</h3>
<p>To view this course you'll need to upgrade your membership</p>
<br>
<hr>
{% endif %}
</div>
</div>
</div>
{% endblock content %}
As I can see here, you can have multiple LessonModule for a single Lesson due to one-to-many relation between Lesson and LessonModule(ForeignKey relation basically). You can fetch all the LessonModules like this(using reverse query):
# view
def get(self, request, course_slug, lesson_slug, *args, **kwargs):
lesson = get_object_or_404(Lesson.objects.select_related('course').prefetch_related('lessonmodule_set'), slug=lesson_slug, course__slug=course_slug)
context = {
'object': lesson,
'previous_lesson': lesson.get_previous_by_created_at,
'next_lesson': lesson.get_next_by_created_at,
}
return render(request, "courses/lesson_detail.html", context)
# template
{% for lesson_module in object.lessonmodule_set.all %}
{{ lesson_module.slug }}
{% endfor %}
I am just sending the Lesson object to template, and using reverse query in template to get LessonModule objects.
How do I go about displaying the category name and short description from the category model onto the categories.html page? I ask this because I can't figure it out and I go the code from the internet so a little unclear how it works. So the code right now takes you to a page like example.com/cat-slug and then shows all the post under that category but how do I display the current category's name on that page (categories.html) as along with the category description too?
models.py:
class Category(models.Model):
name = models.CharField(max_length=100)
short_desc = models.CharField(max_length=160)
slug = models.SlugField()
parent = models.ForeignKey('self',blank=True, null=True ,related_name='children', on_delete=models.CASCADE)
class Meta:
unique_together = ('slug', 'parent',)
verbose_name_plural = "categories"
def __str__(self):
full_path = [self.name]
k = self.parent
while k is not None:
full_path.append(k.name)
k = k.parent
return ' -> '.join(full_path[::-1])
class Post(models.Model):
user = models.CharField(max_length=50, default=1)
title = models.CharField(max_length=120)
short_desc = models.CharField(max_length=50)
category = models.ForeignKey('Category', null=True, blank=True, on_delete=models.CASCADE)
tags = TaggableManager()
content = models.CharField(max_length=1000)
publish = models.DateField(auto_now=False, auto_now_add=False,)
slug = models.SlugField(unique=True)
def __str__(self):
return self.title
def get_cat_list(self):
k = self.category
breadcrumb = ["dummy"]
while k is not None:
breadcrumb.append(k.slug)
k = k.parent
for i in range(len(breadcrumb)-1):
breadcrumb[i] = '/'.join(breadcrumb[-1:i-1:-1])
return breadcrumb[-1:0:-1]
views.py:
def show_category(request, hierarchy=None):
category_slug = hierarchy.split('/')
category_queryset = list(Category.objects.all())
all_slugs = [ x.slug for x in category_queryset ]
parent = None
for slug in category_slug:
if slug in all_slugs:
parent = get_object_or_404(Category, slug=slug, parent=parent)
else:
post = get_object_or_404(Post, slug=slug)
post_related = post.tags.similar_objects()
comments = post.comments.filter(active=True)
instance = get_object_or_404(Post, slug=slug)
breadcrumbs_link = instance.get_cat_list()
category_name = [' '.join(i.split('/')[-1].split('-')) for i in breadcrumbs_link]
breadcrumbs = zip(breadcrumbs_link, category_name)
if request.method == 'POST':
comment_form = CommentForm(data=request.POST)
if comment_form.is_valid():
new_comment = comment_form.save(commit=False)
new_comment.post = post
new_comment.save()
else:
comment_form = CommentForm()
context = {
'post': post,
'post_related': post_related,
'comments': comments,
'comment_form': comment_form,
'instance': instance,
'breadcrumbs': breadcrumbs,
}
return render(request, "blog/post_detail.html", context)
context = {
'post_set': parent.post_set.all(),
'sub_categories': parent.children.all(),
}
return render(request, "blog/categories.html", context)
categories.html:
{% extends 'base.html' %}
{% block content %}
Viewing: {{ name }} Category
{{ short_desc }}
<div class="container">
<div class="row">
{% if post_set %}
{% for post in post_set %}
<div class="col-lg-8 col-md-10 mx-auto">
<div class="post-preview">
<a href="/blog/{{ post.slug }}">
<h2 class="post-title">
{{ post.title }}
</h2>
<h3 class="post-subtitle">
{{ post.short_desc }}
</h3>
</a>
<p class="post-meta">Posted by
{{ post.user }} at {{ post.publish }} in {{ post.category }}
<br />
{% for tag in post.tags.all %}
<font class="trigger blue lighten-4" size="3rem">{{ tag.name }}</font>
{% endfor %}
</p>
</div>
<hr>
</div>
{% endfor %}
{% else %}
<p>No post found in this category.</p>
{% endif %}
</div>
</div>
{% endblock %}
I think the implementation of the parent is not right, because in each iteration of loop, the parent will be updated.
for slug in category_slug:
if slug in all_slugs:
parent = get_object_or_404(Category, slug=slug, parent=parent) # <-- with each iteration, value of parent will be updated.
Rather than that, you can define the parent like this:
parent = Category.objects.filter(slug__in=category_slug, parent=None).first()
context = {
'category': parent, # send the category explicitly through the context
'post_set': parent.post_set.all(),
'sub_categories': parent.children.all(),
}
And update the template:
Viewing: {{ category.name }} Category
{{ category.short_desc }}
I am currently trying to implement a tag system into my Django project. I am trying to add the tags within each post, and have a category on the right hand side that displays maybe 10-20 of the tags. I am trying to implement this into the feed view, but i am unsure of how to call the slug for each tag in order to do /posts/tag/feed. So once you click on a tag it will redirect to the slug of the tag. Which would make the tag clickable. I tried to follow the link below but it only shows how to do it with the class view.
https://godjango.com/33-tagging-with-django-taggit/
views.py
def post_feed(request):
if not request.user.is_staff or not request.user.is_superuser:
raise Http404
queryset_list = Post.objects.all()
tags = Tag.objects.all()
query = request.GET.get("q")
if query:
queryset_list = queryset_list.filter(
Q(title__icontains=query)|
Q(tags__icontains=query)|
Q(description__icontains=query)|
Q(user__first_name__icontains=query)|
Q(user__last_name__icontains=query)
).distinct()
paginator = Paginator(queryset_list, 5)
page_request_var = "page"
page = request.GET.get(page_request_var)
try:
queryset = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer, deliver first page.
queryset = paginator.page(1)
except EmptyPage:
# If page is out of range (e.g. 9999), deliver last page of results.
queryset = paginator.page(paginator.num_pages)
context = {
"object_list": queryset,
"title": "List",
"page_request_var": page_request_var,
}
return render(request, "post_feed.html", context)
And here is my url
url(r'^tag/(?P<slug>[-\w]+)/$', post_feed, name='tagged'),
the Tag.objects.all() only pulls up the tags but doesnt request the slugs.
I am unsure of how to add this to my view without changing it.
from django.views.generic import DetailView, ListView
from taggit.models import Tag
from .models import Product
taggit view to add url and query slug:
class TagMixin(object):
def get_context_data(self, kwargs):
context = super(TagMixin, self).get_context_data(kwargs)
context['tags'] = Tag.objects.all()
return context
class ProductDetail(DetailView):
template_name = 'product/detail.html'
context_object_name = 'product'
model = Product
class ProductIndex(TagMixin, ListView):
template_name = 'product/index.html'
model = Product
paginate_by = '10'
queryset = Product.objects.all()
context_object_name = 'products'
class TagIndexView(TagMixin, ListView):
template_name = 'product/index.html'
model = Product
paginate_by = '10'
context_object_name = 'products'
def get_queryset(self):
return Product.objects.filter(tags__slug=self.kwargs.get('slug'))
I have been stuck on this a few days. Any advice would be helpful.
Here is my models.py, sorry had to format it this way to show up as the whole models code.
from django.db import models
from django.db.models import Count, QuerySet, F
from django.utils import timezone
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.core.urlresolvers import reverse
from django.db.models.signals import pre_save
from django.utils.text import slugify
from markdown_deux import markdown
from django.utils.safestring import mark_safe
from taggit.managers import TaggableManager
from comments.models import Comment
def upload_location(instance, filename):
return "%s/%s" %(instance.slug, filename)
class Post(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, default=1 )
title = models.CharField(max_length=75)
slug = models.SlugField(unique=True)
image = models.ImageField(
upload_to=upload_location,
null=True,
blank=True,
width_field="width_field",
height_field="height_field")
height_field = models.IntegerField(default=0)
width_field = models.IntegerField(default=0)
description = models.TextField()
tags = TaggableManager()
public = models.BooleanField(default=False)
updated = models.DateTimeField(auto_now_add=False, auto_now=True)
created = models.DateTimeField(auto_now_add=True, auto_now=False)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("posts:detail", kwargs={"slug": self.slug})
class Meta:
ordering = ["-created", "-updated" ]
def get_markdown(self):
description = self.description
markdown_text = markdown(description)
return mark_safe(markdown_text)
#property
def comments(self):
instance = self
qs = Comment.objects.filter_by_instance(instance)
return qs
#property
def get_content_type(self):
instance = self
content_type = ContentType.objects.get_for_model(instance.__class__)
return content_type
def create_slug(instance, new_slug=None):
slug = slugify(instance.title)
if new_slug is not None:
slug = new_slug
qs = Post.objects.filter(slug=slug).order_by("-id")
exists = qs.exists()
if exists:
new_slug = "%s-%s" %(slug, qs.first().id)
return create_slug(instance, new_slug=new_slug)
return slug
def pre_save_post_receiver(sender, instance, *args, **kwargs):
if not instance.slug:
instance.slug = create_slug(instance)
pre_save.connect(pre_save_post_receiver, sender=Post)
here is my template
<div class="row">
<div class="col-sm-2">
<div class="panel panel-primary">
<div class="panel-heading">
Tags
</div>
<div class="panel-body">
<ul class="list-group">
{% for tag in tags %}
<li></li>
{% empty %}
<li>No Tags</li>
{% endfor %}
</ul>
</div>
</div>
</div>
</div>
<div class="container">
<div class='col-sm-6 col-sm-offset-3'>
<h1> Post Feed </h1>
<form method='GET' action'' class='row'>
<div class='col-sm-6'>
<div class='input-group'>
<input class='form-control' type='text' name='q' placeholder='Search posts' value='{{ request.GET.q }}'/>
<span class='input-group-btn'>
<input class= 'btn btn-default' type='submit' value='Search'/>
</div>
</div>
</form>
{% for obj in object_list %}
<div class="row">
<div class="col-sm-12">
<div class="thumbnail">
{% if obj.image %}
<img src='{{ obj.image.url }}' class='img-responsive' />
{% endif %}
<div class="caption post-detail-item">
<h3><a href='{{ obj.get_absolute_url }}'><strong>{{ obj.title }}</strong></a> <small>{{ obj.created|timesince }} ago</small>
</h3>
{% if obj.user.get_full_name %}<p>Poster: {{ obj.user.get_full_name }}</p>{% endif %}
{{ obj.get_markdown|truncatechars_html:50 }}
<p>Tags: {{ obj.tags|join:" | "|title }}</p>
<p>View</p>
</div>
</div>
</div>
</div>
{% endfor %}
<div class="pagination">
<span class="step-links">
{% if object_list.has_previous %}
<a href="?{{ page_request_var }}={{ object_list.previous_page_number }}{% if request.GET.q %}&
q={{ request.GET.q }}{% endif %}">previous</a>
{% endif %}
<span class="current">
Page {{ object_list.number }} of {{ object_list.paginator.num_pages }}.
</span>
{% if object_list.has_next %}
next
{% endif %}
</span>
</div>
<footer>
<p class="pull-right">Back to top</p>
<p>© 2016 Holms, Inc. · Terms</p>
</footer>
</div>
{% endblock content %}
</div>
Just copy and paste these.
Change the urls.py entry to this:
url(r'^tag/(?P<pk>[-\w]+)/$', tag_list, name='tagged'),
Your post_feed function to this (views.py):
def post_feed(request):
if not request.user.is_staff or not request.user.is_superuser:
raise Http404
queryset = Post.objects.all()
query = request.GET.get("q")
if query: # this is a separate variable (for searching I'm assuming)
queryset_list = queryset_list.filter(
Q(title__icontains=query)|
Q(tags__icontains=query)| # I would consider taking this out. It's gonna cause problems.
Q(description__icontains=query)|
Q(user__first_name__icontains=query)|
Q(user__last_name__icontains=query)
).distinct()
# bring pagination/tag lookup outside of the if query block -- so you don't NEED a query
paginator = Paginator(queryset, 5)
page_request_var = "page"
page = request.GET.get(page_request_var)
try:
queryset = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer, deliver first page.
queryset = paginator.page(1)
except EmptyPage:
# If page is out of range (e.g. 9999), deliver last page of results.
queryset = paginator.page(paginator.num_pages)
context = {
"object_list": queryset,
"tags": tags.objects.all()[0:20], # first 20 tags of all tags
"title": "List",
"page_request_var": page_request_var,
}
return render(request, "post_feed.html", context)
And your new function to show just posts based on a specific tag to this (views.py):
""" modelled after the function above -- so it's easy to understand """
def tag_list(request, tag_id):
if not request.user.is_staff or not request.user.is_superuser:
raise Http404
queryset = Post.objects.filter(tag__id=tag_id)
paginator = Paginator(queryset, 5)
page_request_var = "page"
page = request.GET.get(page_request_var)
try:
queryset = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer, deliver first page.
queryset = paginator.page(1)
except EmptyPage:
# If page is out of range (e.g. 9999), deliver last page of results.
queryset = paginator.page(paginator.num_pages)
context = {
"object_list": queryset,
"tags": tags.objects.all()[0:20], # first 20 tags of all tags
"title": "List",
"page_request_var": page_request_var,
}
return render(request, "post_feed.html", context)
then change the template to (post_feed.html):
<li>{{tag.name}}</li>
also, read this:
https://docs.djangoproject.com/en/1.9/topics/http/urls/