I want make blog where I have categories and posts inside.
Categories should be displayed, and when you click on it, you are redirected to another site where articles of this category are shown.
models.py:
class Category(CMSPlugin):
title = models.CharField(max_length=20, default='category')
def __unicode__(self):
return self.title
class Blog_post(CMSPlugin):
category = models.ForeignKey(Category)
style = models.ForeignKey(Blog_style)
title = models.CharField(max_length=200, default='title')
description = models.CharField(max_length=200,default='description')
image = models.ImageField(upload_to='static', null=True, blank=True)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
published_date = models.DateTimeField(blank=True, null=True)
def publish(self):
self.published_date = timezone.now()
self.save()
def __unicode__(self):
return self.title
views.py
def Blog_list(request):
posts = Blog_post.objects.filter(published_date__lte=timezone.now()).order_by('published_date')
category = Category.objects.all()
return render(request, 'blogspot.html', {'posts': posts, 'category':category})
def post_detail(request, pk):
post = get_object_or_404(Blog_post, pk=pk)
return render(request, 'post_detail.html', {'post': post})
def category_detail(request, pk):
cat = get_object_or_404(Category, id=pk)
post_with_category = Blog_post.objects.filter(category=cat)
return render(request, 'articles.html', {'post_with_category': post_with_category})
blogspot.html
{% for post in posts %}
<h1>{{post.title}}</h1>
<a href="{% url 'category_detail' pk=post.category.id %}" >{{ post.category }}</a>
{{post.title}}
{{ post.description }}
{{ post.image }}
{{ post.text }}{{ post.published_date }}
{% endfor %}
So far works all ok. I can click on {{post.title}} and im redirected to post_detail. Now i want to make same logic with categories. When i click on {{post.category}} i want redirect to articles.html where u can see all articles in specific category.
EDIT:
I inserted code to show posts in categories. I stucked with for loop. If i use loop mentioned in post, I get all posts and categories. The problem is if i have 2 posts in one category and this loop will show 2x "category" in template.
So I edited my for loop.
{% for post in category %}
{{post.title}}
{% endfor %}
If I insert <a href="{% url 'category_detail' pk=post.category.id %}" >{{post.title}} in this loop i get no reverse match.
I tried to modify views.py category_detail
And url should looks like localhost/<category>/
And another question is, what is QRM alternative comand for "select*from Table Where Column_id= id ;
urls.py
url(r'^blog/$', views.Blog_list, name='Blog_list'),
url(r'^blog/(?P<pk>\d+)/$', views.post_detail, name='post_detail'),
If I understand your question, django allows you to reference FK objects through the main object. So, since your post.category is an instance of your Category model, you should be able to use post.category.id to do a reverse lookup, so your template would have something along the lines of:
<a href="{% url 'category_detail' pk=post.category.id %}" >{{ post.category }}</a>
Then, in your category_detail view you would just use the pk to get the lookup:
cat = get_object_or_404(Category, id=pk)
post_with_category = Blog_post.objects.filter(category=cat)
Then you could display the list of posts on the new url link that have the same category, via your new post_with_category object list.
EDIT:
Your urls would want to include something like the following for the above html href tag to work:
url(r'^cat/(?P<pk>[0-9]+)/$', views.category_detail, name='category_detail'),
Related
Homefeed is the page where i query all the blogposts
In this project, any user that sees a blogpost that they are interest in can submit their interest to the post. 1 user can only submit 1 interest to that blogpost, but they can submit as many interest as they want to different blogposts.
Right now in my home.html, I am trying to make it such that if YOU have submitted interest,(aka your interest status is at pending or accept or decline) for that particular blog post, you will see the view interest button instead of the submit interest button.
But I am facing a problem because in my views, I am querying for blog_posts = BlogPost.objects.all() and not blog_post = get_object_or_404(BlogPost, slug=slug). As such, how am I able to query whether or not for the particular blogpost, the user has already submitted an interest in my template to determine which button should show in my home.html? Thanks, and also I dont want to change the url at all :)
views.py
def home_feed_view(request, *args, **kwargs):
context = {}
blog_posts = BlogPost.objects.all()
context['blog_posts'] = blog_posts
page = pageFilter(request.GET, queryset=BlogPost.objects.exclude(author_id=request.user.id).order_by('date_updated'))
context['page'] = page
paginated_page = Paginator(page.qs, 4)
page = request.GET.get('page')
page_obj = paginated_page.get_page(page)
context['page_obj'] = page_obj
return render(request, "HomeFeed/snippets/home.html", context)
home.html
{% for post in page_obj %}
{% if post.interest_set.exists and request.user.is_authenticated %}
<a class="btn btn-info btn-sm" href="{% url 'HomeFeed:submitinterest' post.slug %}">View Interest</a>
{% else %}
<a class="btn btn-warning btn-sm" href="{% url 'HomeFeed:submitinterest' post.slug %}">Submit Interest</a>
{% endif %}
{% endfor %}
urls.py
path('', home_feed_view , name= "main"),
models.py
class Account(AbstractBaseUser):
email = models.EmailField(verbose_name="email", max_length=60, unique=True)
username = models.CharField(max_length=30, unique=True)
class BlogPost(models.Model):
title = models.CharField(max_length=50, null=False, blank=False, unique=True)
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
slug = models.SlugField(blank=True, unique=True)
class Interest(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
blog_post = models.ForeignKey(BlogPost, on_delete=models.CASCADE)
class InterestInvite(models.Model):
ACCEPT = "ACCEPT"
DECLINE = "DECLINE"
PENDING = "PENDING"
STATUS_CHOICES = [
(ACCEPT, "accept"),
(DECLINE, "decline"),
(PENDING, "pending"),
]
interest = models.OneToOneField(Interest, on_delete=models.CASCADE, related_name="interest_invite")
status = models.CharField(max_length=25, choices=STATUS_CHOICES, default=PENDING)
objects= models.Manager
views.py
type = TypeFilter(request.GET, queryset=BlogPost.objects.exclude((Q(author_id__in=request.user.blocked_users.all()) | Q(author = request.user))).order_by('date_updated')).annotate(user_has_interest=Case(When(interest__user=request.user, then=Value(True)), default=False, output_field=BooleanField()))
Using Django filters:
filters.py
class TypeofIdeaFilter(django_filters.FilterSet):
title = django_filters.CharFilter(field_name="title", lookup_expr='icontains')
class Meta:
model = BlogPost
You can annotate a field on your query:
from django.db.models import Case, When, Value, BooleanField
blog_posts = BlogPost.objects.all().annotate(
user_has_interest=Case(When(interest__user=request.user, then=Value(True)), default=False, output_field=BooleanField())
)
Now you can check in your template using if-else:
{% if post.user_has_interest %}
Something
{% else %}
Something else
{% endif %}
As Pierre mentioned template tags also achieve this (alternate for annotate answerd by Abdul).
Firstly create the file structure. Go into the app directory where the tag is needed, and add these files:
templatetags
templatetags/__init__.py
templatetags/blog_tags.py
The templatetags/blog_tags.py file:
from django import template
register = template.Library()
#register.simple_tag
def interest_submitted(blog_id, user_id):
if Interest.objects.filter(blog_post__id=blog_id, user_id=user_id).exists():
return True
else:
return False
In the template:
{% load blog_tags %} <!--don't forget to load the blog_tags.py file in the template. -->
{% for post in page_obj %}
{% interest_submitted post.id request.user.id as result %}
{% if result and request.user.is_authenticated %}
<a class="btn btn-info btn-sm" href="{% url 'HomeFeed:submitinterest' post.slug %}">View Interest</a>
{% else %}
<a class="btn btn-warning btn-sm" href="{% url 'HomeFeed:submitinterest' post.slug %}">Submit Interest</a>
{% endif %}
{% endfor %}
You can use a try instead of get_object_or_404(). That way you can use different logic and return different contexts if the object does not exist
pseudocode:
context = {}
try:
blog_posts = BlogPost.objects.all()
...
context = "Something"
return render(request, "HomeFeed/snippets/home.html", context)
except BlogPost.DoesNotExist:
context = "Something else"
return render(request, "HomeFeed/snippets/home.html", context)
lets's say that i have three categories (tutorials, news, jobs).
and i have class based views to list all posts, list posts by category and create new posts.
and sure post is the same model and fields to all categories.
my problem is :
if user was in category list template (let's say tutorial) .. i want the user when he create new post .. it is saved directly to tutorial category .. and if user was in list template (let's say news) .. he will create new post which will be saved directly to news category.
i mean create new post saved directly to current category.
i believe i will use (pass url parameter to class based views) but actually i failed to do that .. and i searched tonnage of questions without got what i want.
can any body help .. with sample please.
models.py
class Category(models.Model):
name = models.CharField(max_length=50)
slug = models.SlugField(max_length=50, unique=True)
def save(self, *args, **kwargs):
if not self.slug and self.name:
self.slug = slugify(self.name)
super(Category, self).save(*args, **kwargs)
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
category = models.ForeignKey(Category, on_delete=models.CASCADE, null=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
views.py
def PostListView(request, category_slug=None):
category = None
posts = Post.objects.all().prefetch_related().annotate(commentscountperpost=Count('comments'))
categories = Category.objects.prefetch_related().annotate(total_product_category=Count('post'))
if category_slug:
category = Category.objects.get(slug=category_slug)
posts = posts.filter(category=category)
context = {
'title': 'Home Page',
'posts': posts,
'total_posts': total_posts,
'categories': categories,
'category': category,}
return render(request, 'blog/index.html', context)
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
template_name = 'blog/new_post.html'
form_class = PostCreateForm
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
forms.py
class PostCreateForm(forms.ModelForm):
title = forms.CharField(label='Title')
content = forms.CharField(label='Content', widget=forms.Textarea)
class Meta:
model = Post
fields = ['title', 'content']
urls.py
path('index_list/', PostListView, name='list'),
path('<slug:category_slug>', PostListView, name='post_category_list'),
path('new_post/', PostCreateView.as_view(), name='new_post'),
new_post.html template
{% extends 'base.html' %}
{% block content %}
{% load crispy_forms_tags %}
<div class="border p-4 mb-5">
<legend class="border-bottom pb-1 mb-3">New Post </legend>
<form method="POST">
{% csrf_token %}
{{form|crispy}}
<input class="btn btn-secondary mt-4" type="submit" value="Add New Post">
</form>
</div>
{% endblock content %}
list.html template
{% for category in categories %}
<h5><a class="text-primary" href="{% url 'post_category_list' category.slug %}">
{{ category.name }} ({{ category.total_product_category }})</a></h5>
{% endfor %}
<h5>{{ total_posts }} Total Posts </h5>
{% if category %}
New {{ category }}
{% endif %}
You can try like this:
class PostCreateView(generic.CreateView):
model = Post
template_name = 'blog/new_post.html'
form_class = CreatePostForm
slug_url_kwarg = 'slug'
def form_valid(self, form):
category = Category.objects.get(slug=self.kwargs['slug'])
form.instance.category = category
form.instance.author = self.request.user
return super(PostCreateView, self).form_valid(form)
And in the urls
path('new_post/<slug>/', PostCreateView.as_view(), name='new_post'),
yes i found it , depending on arjun answer, greate thanks for arjun
in list posts per category template change :
New {{ category }}
to:
New {{ category }}
and it works fine,
thanks.
My project is here github Project
I'm getting this error.
ValueError at /salesapp/add/ashton-aged-maduro/
The view salesapp.views.add_CartItem didn't return an HttpResponse object. It returned None instead.
I get this error when I click the 'Add to Cart' button on my singleproduct.html template which calls the ProductAddToCart form. The view is add_CartItem.
I also get the error Field is required when I don't initially set the form values. I'm just stuck now.
This is models.py
class Product(models.Model):
itemid = models.CharField(max_length=128, unique=True)
itemname = models.CharField(max_length=128)
brand = models.CharField(max_length=128)
image = models.ImageField(upload_to='static/images/')
notes = models.CharField(max_length=250)
price = models.IntegerField()
slug = models.SlugField(unique=True)
def save(self, *args, **kwargs):
self.slug = slugify(self.itemname)
super(Product, self).save(*args, **kwargs)
def __str__(self):
return self.itemname
class CartItem(models.Model):
cart_id = models.CharField(max_length=50)
date_added = models.DateTimeField(auto_now_add=True)
quantity = models.IntegerField(default=1)
itemid = models.ForeignKey('Product', unique=False)
class Meta:
db_table = 'cart_items'
ordering = ['date_added']
def name(self):
return self.product.name
My forms.py
class ProductAddToCartForm(forms.ModelForm):
cart_id = forms.CharField(max_length=50)
date_added = forms.DateTimeField()
quantity = forms.IntegerField()
slug = forms.CharField(widget=forms.HiddenInput(), required=False)
#itemid = models.ForeignKey('Product', unique=False)
class Meta:
model = CartItem
fields = ('cart_id', 'date_added', 'quantity', 'slug', 'itemid', )
My views.py
def add_CartItem(request, product_name_slug):
print('In add_CartItem --------------------')
form = ProductAddToCartForm(request.POST)
p = Product.objects.get(slug=product_name_slug)
form = ProductAddToCartForm(initial={'cart_id': 123, 'date_added':date.date.today(), 'quantity': 1, 'slug':p.slug, 'id':p.id, 'itemid':p.itemid})
form.save(commit=False)
print(form)
print(p.slug)
print(p.id)
print(p.itemid)
if form.is_valid():
print(p)
print('In form.is_valid()--------------------------------')
ci = CartItem.objects.create(cart_id=1, date_added=date.date.today(), quantity=1, itemid=p)
form.save(commit=True)
return index(request)
else:
print(form.errors) #return render(request, 'salesapp/errors.html', {'form': form})
My urls.py
from django.conf.urls import url
from salesapp import views
urlpatterns = [
url(r'^$', views.index, name='index'),
url(r'about/', views.about, name='about'),
url(r'customers/', views.customers, name='customers'),
url(r'products/', views.products, name='products'),
url(r'^add_product/$', views.add_product, name='add_product'),
url(r'^add_customer/$', views.add_customer, name='add_customer'),
url(r'items/(?P<product_name_slug>[\w\-]+)/$', views.show_product, name='show_product'),
url(r'^add/(?P<product_name_slug>[\w\-]+)/$', views.add_CartItem, name='add_CartItem'),
#url(r'^cart/$', views.show_cart, name='show_cart'),
#url(r'^register/$', views.register, name='register'),
#url(r'^login/$', views.user_login, name='login'),
#url(r'^logout/$', views.user_logout, name='logout'),
#url(r'^restricted/', views.restricted, name='restricted'),
]
and my template where I want to display the ProductAddToCartForm but add a product to the CartItem table.
<!DOCTYPE html>
{% extends 'salesapp/base.html' %}
{% load staticfiles %}
{% block title_block %}
{{ product.itemname }}
{% endblock %}
{% block body_block %}
<div>
<div>
<ul style="list-style:none; text-align:center;">
<li style="float:left; width:25%; margin:20px;">
<img src="/{{ product.image }}"/>
<div>
<b>{{ product.itemname }}</b><br/>
Price per cigar:<br/>
<b>${{ product.price }}</b>
<p>{{ product.notes }}</p>
</div>
<form method="post" action="/salesapp/add/{{ product.slug }}/" class="cart">
{% csrf_token %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for field in form.visible_fields %}
{{ field.errors }}
{{ field.help_text }}
{{ field }}
{% endfor %}
<br />
<input type="submit" value="Add To Cart" name="submit" alt="Add To Cart" />
</form>
<div class="cb"></div>
</li>
</ul>
</div>
<!-- More code -->
</div>
{% endblock %}
A view function in django should either return a json or a dict or can return a Webpage
You can either do one of following
1) return a json or dict
return {"value1":"value","value2":"value"}
2)redirect to a web page
return redirect('/some/url/')
3)return Http response
return HttpResponse(status=<status code>,content="some content")
4)render a template
t = loader.get_template('myapp/index.html')
c = {'foo': 'bar'}
return HttpResponse(t.render(c, request),
content_type='application/xhtml+xml')
Because you did not return any response to the view,
According to the docs
A view function is simply a Python function that
takes a Web request and returns a Web response.
You need to return to use render method for initial rendering of form and for redirection to another view you can use redirect method of Django.
A view function must return an HttpResponse. For example, if the process was successfull and you dont want to return anything, you can return HttpResponse(status=200)
When a view handles forms, you have to split GET and POST requests. In the GET part you need to instantiate the form without data. In the POST part you fill the form with request.POST data. And this data must have ALL mandatory fields. A typical view function scheme to handle a form is the folowing:
def view(request):
if request.method == "GET":
form = MyForm()
return ...
if request.method == "POST":
form = MyForm(request.POST)
form.save()
return ...
In your template, you have to show all form fields. Then, all form fields will be passed with the request. If you dont, you need to fill the form fields in the view.
first delete the prints, almost when you make a question, are useless in Django
def add_CartItem(request, product_name_slug):
form = ProductAddToCartForm(request.POST)
if request.method == 'POST':
if form.is_valid():
ci = CartItem.objects.create(cart_id=1, date_added=date.date.today(), quantity=1, itemid=p)
ci.save()#you save your model changes, not your form
return HttpResponseRedirect(reverse('your:url'))#your return the success url or the same
else:
#here error, if form isn't valid
else:
form = ProductAddToCartForm(request.POST)
return render(request, 'your/template.html', {'form': form})
That is the correct way to work with forms in Django, first you must make a if statement asking to the browser if is a post request or a normal request, if is post request, take the data from the forms and are adding to the database, if not, Django return a empty template form.
Let me know if you problem solve
I have a blog app with a list of the latest post titles. Now the list item should be linked to its content. My problem (similar post) is that if the title has some spaces i get a url with spaces if i use:
<a href="{{ i.id }}/{{ i.title }}">{{ i.title }}
in my template. I could use an additional URLField but i dont want to create the url-friendly title manually. What's the common way to do this?
My models.py
class Post(models.Model):
title = models.CharField(max_length=100)
...
def __unicode__(self):
return self.title
My view.py
def recentlyBlogged(request):
lastPosts = Post.objects.filter(publication__gt = datetime.now() - timedelta(days=30))
return render(request, "blog/blog.html", {'Posts': lastPosts})
My template
{% for i in Posts %}
<ul id="latestPostsList">
<li class="latestPostsListItem">{{ i }}</li>
{% endfor %}
</ul>
You are looking for a slug.
Try this
from django.template.defaultfilters import slugify
class Post(models.Model):
title = models.CharField(max_length=100)
...
def __unicode__(self):
return self.title
def get_absolute_url(self):
return reverse('post_url', args=(slugify(self.title), ))
and in the template,
{{ i.title }}
You might have to modify urls.py accordingly too
url(r'post_url/(?P<slug>[\w-]+)/', view_name, name="post_url")
I am using Django 1.4.5.
I get an error when I access the detail of entry via admin's page.
"NoReverseMatch at /admin/r/12/1/": Reverse for 'detail' with
arguments '()' and keyword arguments '{'category': u'category',
'slug': u'entry-test'}' not found
And get nothing/blank page when access the detail of entry on front's page (/category/entry-test/).
And what's the right of models & URLname pattern if I want to access the entry detail via sub-category (/category/subcategory/entry-test/)?
Model snippet:
class Entry (models.Model):
title = models.CharField()
slug = models.SlugField()
category = models.ForeignKey('entry.Category')
def __unicode__(self):
return self.title
#models.permalink
def get_absolute_url(self):
return ('entry.views.detail', (), {'category': self.category.slug, 'slug': self.slug})
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.title)
return super(Entry, self).save(*args, **kwargs)
class Category(models.Model):
title = models.CharField()
slug = models.SlugField()
parent = models.ForeignKey('self', blank=True, null=True, related_name='children')
def __unicode__(self):
if self.parent:
return self.parent.title, self.title
return self.title
#permalink
def get_absolute_url(self):
return ('entry.views.category', (), {'slug': self.slug})
Views snippet:
def category(request):
category = Category.objects.all()
return render_to_response('category.html', locals(), context_instance=RequestContext(request))
def list(request, slug_id):
category = get_object_or_404(Category, slug=slug_id)
list = Entry.objects.filter(category=category)
return render_to_response("list.html", locals(), context_instance=RequestContext(request))
def detail(request, category_id, slug_id):
entry = Entry.objects.filter(slug=slug_id)
category = Category.objects.filter(slug=category_id)
return render_to_response('detail.html', locals(), context_instance=RequestContext(request))
URLs snippet:
urlpatterns = patterns('entry.views',
(r'^$', 'category', name='entry-category'),
url(r'^(?P<slug_id>[-\w]+)/$', 'list', name='entry-list'),
url(r'^(?P<category_id>[-\w]+)/(?P<slug_id>[-\w]+)/$', 'detail', name='entry-detail'),
Template snippet:
category.html
<ul>
{% for category in category %}
<li>{{ category.title }} ({{ category.entry_set.all.count }})</li>
{% endfor %}
</ul>
list.html
<ul>
{% for entry in list %}
<li>{{ entry.title }}</li>
{% endfor %}
</ul>
detail.html
<ul>
{% for entry in entry.category %}
<li>{{ entry.title }}
<br />{{ entry.description }}</li>
{% endfor %}
</ul>
Where and what I could be doing wrong?
If any one can help I'd appreciate it! Please help me.
Your keyword arguments for the url are category_id and slug_id, but you are passing in category and slug in your get_absolute_url method. This version should work:
#models.permalink
def get_absolute_url(self):
return ('entry.views.detail',
(), {'category_id': self.category.slug, 'slug_id': self.slug})
Also consider renaming your arguments. id is generally numeric and slug is alphanumeric.
For your blank page problem (from your comment):
Your URL /category/entry-test/ - this will map to your detail view if the snippet your pasted in your question is in your main urls.py. If you have no matching results (the .filter() call has no results), you'll see a "blank" page, because the <ul> will not have any li elements.
If in your main urls.py, you have something like url('^category/', include(some.other.urls)), then the URL will map to your list view, and you have the same issue - the .filter() is not returning any results, and hence you see a "blank" page.
I do little mistake on detail template:
<ul>
{% for entry in entry %}
<li>
{{ entry.title }}
<br />
{{ entry.description }}
</li>
{% endfor %}
</ul>
The problem is solved now.