search query result in django - django

Here is my model
class News(models.Model):
title = models.CharField(max_length=500)
published_date = models.CharField(max_length=100)
description = models.TextField()
details = models.TextField()
status = models.BooleanField(default=False)
crawler = models.ForeignKey('Crawler')
category = models.ForeignKey('Category')
And the view for the news is
class DisplayNewsView(TemplateView):
template_name = "news_listing.html"
#paginate_by = 10
def get_context_data(self, **kwargs):
context = super(DisplayNewsView, self).get_context_data(**kwargs)
categories_list = Category.objects.all().filter(created_by=self.request.user)
context['news_list'] = []
for categories in categories_list:
print(categories)
for crawler in categories.crawlers.get_queryset():
#print(categories)
print(crawler)
crawler_list = News.objects.filter(
Q(category=categories),
Q(crawler=crawler) | Q(crawler=crawler))
#print(crawler_list)
context['news_list'].append(crawler_list)
return context
I have displayed the news in template.
I want to search the news according to time. I mean the news from "date" to "date" as per the published date in news model.
My model for category is
class Category(models.Model):
category = models.CharField(max_length=50)
identifier = models.CharField(max_length=50, unique=True)
level_1_words = models.TextField()
level_2_words = models.TextField()
created_by = models.ForeignKey(User,null=True)
crawlers = models.ManyToManyField('Crawler',related_name='crawler_name')
class Meta:
verbose_name_plural = "Categories"
def __unicode__(self): # Python 3: def __str__(self):
return self.category
Can anyone plz help to to search the news according to publish date.
I want it to be done like from "date" to "date" submit.
When user click the submit button I want the news to be filtered..
It would be great help if someone tell me how to download the searched news in csv too. thanx in advance

You first need to make that field of the date a DateField instead of CharField
This is how to query date range in django
News.objects.filter(pub_date__lte=datetime(2014, 5, 30), pub_date__gte=datetime(2014, 1, 30))
Another example would be
News.objects.filter(pub_date__lte=datetime(2014, 5, 30), pub_date__gte=datetime(2014, 1, 30)).exclude(datetime.date.today())
for more information on django queries check out the docs # Making queries in Django

Related

Django - Altering list view based on link that is pressed

I have a navbar above my products on my fake e commerce website that I am building. I render this view through list view. In this navbar I have a search area that works fine, and want to add the ability to filter the results based on what is pressed. For example, I have styles: realism, portrait, etc in the navbar. When I click on realism I want the results to change to all realism paintings that are in the DB.
My question is what would be the best way to accomplish this? Could I alter the original list view? Or would I just need to create an entirely new view based on the specific queries I need? Any help is greatly appreciated!
Also attached will be a picture to show you what I mean in case my explanation wasn't the best.
view:
class AllProductView(ListView):
template_name = 'my_site/all_products.html'
model = Product
ordering=['id']
paginate_by = 9
context_object_name = 'products'
product model as requested:
class Product(models.Model):
name = models.CharField(max_length=120)
shortened_name = models.CharField(max_length=50)
date_of_creation = models.CharField(max_length=20, null=True)
history = models.CharField(max_length=255)
price = models.DecimalField(max_digits=9, decimal_places=2)
discount_price = models.FloatField(blank=True, null=True)
style = models.CharField(max_length=50)
artist = models.ForeignKey(Artist, on_delete=models.SET_NULL, null=True)
image = models.ImageField(upload_to='images', null=True)
slug = models.SlugField()
You can filter by overriding the .get_queryset(…) method [Django-doc]:
class AllProductView(ListView):
template_name = 'my_site/all_products.html'
model = Product
ordering=['id']
paginate_by = 9
context_object_name = 'products'
def get_queryset(self, *args, **kwargs):
qs = super().get_queryset(*args, **kwargs)
fltr = self.kwargs['style']
if fltr != 'all':
qs = qs.filter(style=self.kwargs['style'])
return qs
In your urls.py, you can then use a <str:…> path converter to determine the style we want to filter on, so:
path('products/<str:style>/', AllProductView.as_view(), name='products')
In the templates, we can then write URLs with:
Realism
This will than link to a page that only shows Products with style='realism'.

How to mix two queries to one as dropdown elements

I need to join and pass two queries of category and subcategory as elements of an autocomplete select2 dropdown as my django form field as below:
This is my form:
class CategoriesAutocomplete(autocomplete.Select2QuerySetView):
def get_queryset(self):
if not self.request.user.is_authenticated:
return Categories.objects.none()
qs = Categories.objects.all()
if self.q:
qs = qs.filter(Q(name__icontains=self.q))
return qs
def get_result_label(self, item):
return format_html( item.name)
class categories_form(forms.ModelForm):
categories = forms.ModelChoiceField(
queryset= Categories.objects.none(),
widget= autocomplete.ModelSelect2(
url='load_categories',
attrs={
'data-placeholder': 'Select a category',
'data-html': True,
'style': 'min-width: 15em !important;',
}
)
)
class Meta:
model = Post
fields = ['categories']
def __init__(self, *args, **kwargs):
super(category_form, self).__init__(*args, **kwargs)
self.fields['categories'].queryset = Categories.objects.all()
and in url:
path('ajax/categories-autocomplete', CategoriesAutocomplete.as_view(), name='load_categories'),
for category model:
class Categories(models.Model):
name = models.CharField(max_length=100)
brief = models.TextField(null=True)
slug = models.SlugField(max_length=200)
date = models.DateTimeField(default=timezone.now)
class Meta:
db_table = "categories"
for sub-category model:
class Sub_Categories(models.Model):
name = models.CharField(max_length=100)
brief = models.TextField(null=True)
slug = models.SlugField(max_length=200)
date = models.DateTimeField(default=timezone.now)
class Meta:
db_table = "sub_categories"
model to connect category with sub-categories:
class Categories_Sub_Categories(models.Model):
category = models.OneToOneField(Categories, primary_key=True, on_delete=models.CASCADE)
sub_cat = models.OneToOneField(Sub_Categories, on_delete=models.CASCADE)
class Meta:
db_table = "categories_sub-categories"
and in Post model:
class Post(models.Model):
title = models.CharField(max_length=50)
descript = HTMLField(blank=True, null=True)
categories = models.ForeignKey(Categories, blank=True, null=True, on_delete=models.CASCADE)
subcategories = models.ForeignKey(Sub_Categories, blank=True, null=True, on_delete=models.CASCADE)
author = models.ForeignKey(User, on_delete=models.CASCADE)
date = models.DateTimeField(default=timezone.now)
UPDATE:
The answer provided by #ha-neul is working just there is a bug. I show it with an example:
This is what expect in the dropdown:
**Asia**
China
Malaysia
India
Tajikistan
Iran
Qatar
**Europe**
Germany
Italy
Spain
Netherlands
France
**Africa**
Gana
...
But this is what I see:
**Asia**
China
Malaysia
**Europe**
Netherlands
France
Sweden
Norway
**Asia**
India
Tajikistan
Iran
**Europe**
Germany
Italy
**Asia**
Qatar
**Africa**
Gana
...
**America**
....
**Europe**
Spain
in the SubCategory table I have something like:
id ........... category_id
1 1
2 1
3 1
4 1
5 3
6 1
7 2
I am following this package. Any idea to make me even closer to the solution would be appreciated!!
If this is what you want to achieve, then:
The short answer is you should
have your subcategory with ForeignKeyField referring to
Category model.
use Select2GroupQuerySetView instead of Select2QuerySetView.
But implementing it is a bit complicated.
First of all, although django-autocomplete-light's source code has Select2GroupQuerySetView , somehow you cannot just use it as autocomplete.Select2GroupQuerySetView. So, you have to write the same thing in your own views.py. In addition, the source code has a typo, so you need to fix it.
Step 1. In models.py:
class Category(models.Model):
name = models.CharField(max_length=100)
brief = models.TextField(null=True)
slug = models.SlugField(max_length=200)
date = models.DateTimeField(default=timezone.now)
class SubCategory(models.Model):
##################
#You need add this line, so there is a one-to-many relationship
category = models.ForeignKey(Category, on_delete=models.CASCADE,
related_name='subcategories')
###############
name = models.CharField(max_length=100)
brief = models.TextField(null=True)
slug = models.SlugField(max_length=200)
date = models.DateTimeField(default=timezone.now)
Step2.in views.py copy-paste Select2GroupQuerySetView code and fix a typo
# import collections, so Select2GroupQuerySetView can work
import collections
class Select2GroupQuerySetView(autocomplete.Select2QuerySetView):
group_by_related = None
related_field_name = 'name'
def get_results(self, context):
if not self.group_by_related:
raise ImproperlyConfigured("Missing group_by_related.")
groups = collections.OrderedDict()
object_list = context['object_list']
print(object_list)
object_list = object_list.annotate(
group_name=F(f'{self.group_by_related}__{self.related_field_name}'))
for result in object_list:
group_name = getattr(result, 'group_name')
groups.setdefault(group_name, [])
groups[group_name].append(result)
return [{
'id': None,
'text': group,
'children': [{
'id': result.id,
'text': getattr(result, self.related_field_name),
# this is the line I had to comment out
#'title': result.descricao
} for result in results]
} for group, results in groups.items()]
3. write your own view using Select2GroupQuerySetView
class SubCategoryAutocomplete(Select2GroupQuerySetView):
print('under subcategory autocomplete')
group_by_related = 'category' # this is the fieldname of ForeignKey
related_field_name = 'name' # this is the fieldname that you want to show.
def get_queryset(self):
##### Here is what you normally put... I am showing the minimum code.
qs = SubCategory.objects.all()
if self.q:
qs = qs.filter(name__istartswith=self.q)
return qs
Howe to use this view in a your project?
1. Say you have a Post model as below, with subcategory as a ForeignKey.
class Post(models.Model):
title = models.CharField(max_length=100)
subcategory = models.ForeignKey(SubCategory,on_delete=models.CASCADE)
def __str__(self):
return self.title
2. You will generate a PostForm that contains the subcategory autocomplete field.
in forms.py
from django.conf.urls import url
class PostForm(forms.ModelForm):
subcategory = forms.ModelChoiceField(
queryset=Subcategory.objects.all(),
widget=autocomplete.ModelSelect2(url='subcategory-autocomplete')
)
class Meta:
model= Post
fields = ('title','subcategory')
You will generate a CreatePostView using generic CreateView
from django.views.generic.edit import CreateView
class CreatePostView(CreateView):
model=Post
template_name='yourapp/yourtemplate.html'# need to change
form_class=PostForm
success_url = '/'
Now, in your urls.py, one url for CreatePostView another one for autocomplete view.
urlpatterns = [
url(
r'^subcategory-autocomplete/$',
SubCategoryAutocomplete.as_view(),
name='subcategory-autocomplete',
),
path('post/create',CreatePostView.as_view(), name='create_post'),
it's all set, you will go to post/create and see a PostForm with subcategories autocomplete field.
OP had a weird grouping behavior after using the code above. In his comment, he mentioned:
Added a .order_by(category_id')` to the qs and fixed it.
You do not need to create this model to make a relation
Categories_Sub_Categories
just create a one-to-many field (categories) in Sub_Categories model and put Categories model there (foreign), it will do that automatically, then retrieve data like this (in backend)
categories = Categories.objects.all()
you will get all categories with Sub_Categories object here, pass it to frontend and loop through it (in front-end)
for category in categories:
sub_categories = category.sub_categories_set.all()
To make SubCategory you can have ForeignKey to self.
Another point, you'll need to use prefetch_related from main model (Post) to be able to "join" Category/SubCategory there.
Here is an example how this should look like:
# forms.py
from django import forms
from django.db.models import Prefetch
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = [...]
def __init__(self, *args, **kwargs):
super(PostForm, self).__init__(*args, **kwargs)
cats = Category.objects \
.filter(category__isnull=True) \
.order_by('order') \
.prefetch_related(Prefetch('subcategories',
queryset=Category.objects.order_by('order')))
self.fields['subcategory'].choices = \
[("", self.fields['subcategory'].empty_label)] \
+ [(c.name, [
(self.fields['subcategory'].prepare_value(sc),
self.fields['subcategory'].label_from_instance(sc))
for sc in c.subcategories.all()
]) for c in cats]
# models.py
class Category(models.Model):
category = models.ForeignKey('self', null=True, on_delete=models.CASCADE,
related_name='subcategories', related_query_name='subcategory')
class Post(models.Model):
subcategory = models.ForeignKey(Category, on_delete=models.CASCADE,
related_name='posts', related_query_name='post')

Django query categories, exclude those without posts

I have these two models, post and categories.
class Category(models.Model):
""" Categories """
name = models.CharField(max_length = 80, help_text="Enter a descriptive and unique category name.")
slug = models.SlugField(max_length = 250, help_text="The slug is used to link category pages.")
class Post(models.Model):
""" Blog posts """
author = models.ForeignKey(User, related_name='blog_posts', on_delete = models.CASCADE)
category = models.ForeignKey(Category, related_name = 'blog_posts', on_delete = models.CASCADE)
title = models.CharField(max_length=250)
body = models.TextField(help_text = "Type your blog post using plain text or mark-down format.")
I am trying to query all the categories that have posts, excluding categories which don't have yet posts. The SQL equivalent of:
SELECT * FROM category WHERE id IN (SELECT DISTINCT(category_id) FROM post)
Many thanks!
You can use annotation to count the posts and then filter based on the result:
from django.db.models import Count
Category.objects.annotate(posts_count=Count("blog_posts")).filter(post_count__gt=0)

Django inline model formset with 2 models

First of all, please forgive for my newbie questions. I did copy most of the code, and try to understand from Django documents.
Code as below:
models.py
class Order(models.Model):
ORDER_CHOICES = (
('import', 'IMPORT'),
('export', 'EXPORT')
)
storage = models.ForeignKey(Storage, on_delete=models.PROTECT)
order_type = models.CharField(max_length=6, choices=ORDER_CHOICES)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now_add=True)
class Item(models.Model):
def random_barcode():
return str(random.randint(10000000, 99999999))
type = models.ForeignKey(Type, on_delete=models.CASCADE)
order = models.ForeignKey(Order, on_delete=models.CASCADE, null=True)
brand = models.ForeignKey(Brand, on_delete=models.CASCADE)
item_name = models.CharField(max_length=50, help_text='Name of goods, max 50 characters')
barcode = models.CharField(max_length=8, default=random_barcode, unique=True)
production_date = models.DateField()
expired_date = models.DateField()
def __str__(self):
return self.item_type
forms.py
class ItemForm(ModelForm):
class Meta:
model = Item
exclude = ['order',]
fields = ['type', 'brand', 'item_name', 'production_date', 'expired_date']
ItemFormSet = inlineformset_factory(Order, Item, form=ItemForm, extra=1)
views.py
class CreatePO(CreateView):
model = Order
context_object_name = 'orders'
template_name = 'storages/create_po.html'
fields = ['order_type', 'storage',]
*#dun't know how to write below code....*
1st question: how to use inline formset to write the CreatePO view?
2nd question: I need my create PO template as below picture, how to add a "Quantity" field?
This kind of template need Javascript, right? Any alternative solution? I have no knowledge with javascript.
First of all, move the def random_barcode(): before def __str__(self): it looks so ugly formated code.
Then let's have a look in your pic, if you haven't proper experience with Javascript you can use Admin Views from Django, it's much more simple and supported by Django 2.1. Read more if you would like to give permission to everyone in a admin-views page https://docs.djangoproject.com/el/2.1/releases/2.1/#model-view-permission
So quantity will be just added inside Item class
quantity = models.PositiveSmallIntegerField(default=1)
Also for your form, in my opinion, you need modelform_factory, so I suggest to read this one https://docs.djangoproject.com/en/2.1/topics/forms/modelforms/#modelform-factory-function

Foreign key backward navigation in Django

For this example:
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def __unicode__(self):
return self.name
class Author(models.Model):
name = models.CharField(max_length=50)
email = models.EmailField()
def __unicode__(self):
return self.name
class Entry(models.Model):
blog = models.ForeignKey(Blog, related_name='entries')
headline = models.CharField(max_length=255)
body_text = models.TextField()
pub_date = models.DateTimeField()
mod_date = models.DateTimeField()
authors = models.ManyToManyField(Author)
n_comments = models.IntegerField()
n_pingbacks = models.IntegerField()
rating = models.IntegerField()
def __unicode__(self):
return self.headline
So, how to find all Blogs with its Entries?
I want to do something like this
q = Blog.objects.all().entries.filter(...)
But it gave me an error. So does Django only supports to use the backward navigation properties for only one object rather than a set of objects?
Let's say you want to filter on the entries of all blogs, and get the blogs that have entries you require:
Blog.objects.filter(entry__headline__icontains="cats").distinct()
There is a one blog to many entries relationship here, but this query works and gives you the blogs that have entries with cats in headlines.
If you want to get all entries associated with each Blog, you can do something like:
blogs = Blog.objects.all()
for blog in blogs:
blog.cached_entries = blog.entry_set.all()
Although this looks neat, n + 1 queries will be made -- 1 query for getting all blogs plus n queries for getting each entry associated with the blog.
It is possible to do this in just one query, but you'll have to do more work if you want to segregate entries by blog.
blog_with_entries = []
entries = Entry.objects.order_by('blog')
previous_blog = None
acc_entries = [] # accumulate entries of one blog
for entry in entries:
if previous_blog != entry.blog:
if previous_blog is not None:
blog_with_entries.append((previous_blog, acc_entries))
acc_entries = []
previous_blog = entry.blog
acc_entries.append(entry)
blog_with_entries.append((previous_blog, acc_entries))
Yes.
"Lookups that span relationships"