How to join table if string field is not null - python

I have to show all articles from a law. Besides that, each article can have a description or not. I have to show all articles and yours description, but I dont know how to join descriptions and article when highlight description is not null.
my view:
def details(request, pk):
law = get_object_or_404(Law, pk=pk)
articles = Article.objects.filter(law=pk)
articles = (
Article
.objects
.filter(law=pk)
.annotate(
is_marked=Exists(
Highlight
.objects
.filter(
article_id=OuterRef('id'),
user=request.user
)
)
)
)
context = {
'articles': articles,
}
template_name = 'leis/details.html'
return render(request, template_name, context)
My detail.html:
<div class="article-post">
{% for article in articles %}
{{article}}
{% endfor %}
</div>
That's my model:
class Law(models.Model):
name = models.CharField('Name', max_length=100)
description = models.TextField('Description', blank = True, null=True)
class Article(models.Model):
article = models.TextField('Artigo/Inciso')
number = models.IntegerField('Number', blank=True, null=True)
law = models.ForeignKey(Law, on_delete=models.CASCADE, verbose_name='Law', related_name='articles')
This class is saved with description made by a specific user in a specific article in a specif law:
class Highlight(models.Model):
law = models.ForeignKey(Law, on_delete=models.CASCADE, verbose_name='Law', related_name='highlightArticles')
article = models.ForeignKey(Law, on_delete=models.CASCADE, verbose_name='Article', related_name='highlightLaw')
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, verbose_name='highlightUsers', related_name='highlightUsers')
is_marked = models.BooleanField('Is it marked?', blank=True, default=False)
description = models.TextField('Description', blank = True, null=True)
How can I join the tables to show all the articles with yours specific description made by an specific user?

You can try:
Article.objects.filter(law=pk, highlight__description__isnull=False).annotate(description=(F('highlight__description')))
it will return all Article with highlight description is not null, and can show description in your view with {{ article.description }}

Related

Cannot query "Product": Must be "Comment" instance

I'm trying to add a commenting and replying system to my products model but I can't add replies to comment.
This is being done in the same page where the product details are being shown to the user.
Edit:
I'm getting a Cannot assign "<Product: Test Product>": "Reply.comment" must be a "Comment" instance. error at new_reply = Reply(content=content, author=self.request.user, comment=self.get_object())
views.py:
class ProductFeedbackView(DetailView):
model = Product
template_name = 'store/product_feedback.html'
def get_context_data(self , **kwargs):
data = super().get_context_data(**kwargs)
connected_comments = Comment.objects.filter(product=self.get_object())
number_of_comments = connected_comments.count()
data['comments'] = connected_comments
data['no_of_comments'] = number_of_comments
data['comment_form'] = CommentForm()
connected_replies = Reply.objects.filter(comment=self.get_object())
number_of_replies = connected_replies.count()
data['replies'] = connected_replies
data['no_of_replies'] = number_of_replies
data['reply_form'] = ReplyForm()
return data
def post(self , request , *args , **kwargs):
if self.request.method == 'POST':
reply_form = ReplyForm(self.request.POST)
if reply_form.is_valid():
content = reply_form.cleaned_data['content']
new_reply = Reply(content=content, author=self.request.user, comment=self.get_object())
new_reply.save()
return redirect(self.request.path_info)
if self.request.method == 'POST':
comment_form = CommentForm(self.request.POST)
if comment_form.is_valid():
content = comment_form.cleaned_data['content']
new_comment = Comment(content=content, author=self.request.user, product=self.get_object())
new_comment.save()
return redirect(self.request.path_info)
models.py:
class Product(models.Model):
author = models.ForeignKey(User, default=None, on_delete=models.CASCADE)
title = models.CharField(max_length=120, unique=True)
description = models.CharField(max_length=300, blank=True, null=True)
class Comment(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE, blank=True, null=True, related_name='comments')
author = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True,)
content = models.CharField(max_length=200, null=True, blank=False)
class Reply(models.Model):
comment = models.ForeignKey(Comment, on_delete=models.CASCADE)
author = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True,)
content = models.TextField(null=True, blank=False)
As the error message suggests, you're trying to assign a Product instance to a field that expects a Comment instance.
This is the line where you try to do this:
connected_replies = Reply.objects.filter(comment=self.get_object())
self.get_object() returns a Product instance as you defined model = Product on your View.
To get the replies connected to your product, you will need to loop over all comments and per comment all its replies as you defined these relations as foreignkeys.
For example:
for comment in connected_comments:
comment_replies = Reply.objects.filter(comment=comment)
#Vincent answer is ok, the error is from wrong model passed to filter of Replay model.
But for remedy to make it easier in template for showing comments and replies to those comments i suggest delete from context
data['replies']
data['no_of_replies']
and in template where you loop through comments (just example):
{% for comment in comments %}
<h1>{{comment}}</h1>
{% for reply in comment.reply_set.all %}
<p>{{ reply }} </p>
{% endfor %}
{% endfor %}
use reverse relationship with reply_set.
Oh, and for optimization add prefetch_related to your query:
Comment.objects.filter(product=self.get_object()).prefetch_related('reply_set')

Getting Foreign Key data in Django Admin Add/Change Form

I am trying to customise the Django admin add/change for a project. I have created a model called "Visit" which contains 3 Foreign Key fields: "Customer", "Pet" and "Doctor". The workflow is as follows:
The user creates a customer.
The user creates a pet and associates it with a customer.
The user creates a visit and associates it with a pet and a doctor.
Below is the code for my models.py
class Visit(models.Model):
customer = models.ForeignKey('customer.Customer', on_delete=models.CASCADE)
pet = models.ForeignKey('pet.Pet', on_delete=models.CASCADE)
date = models.DateTimeField()
doctor = models.ForeignKey(
'configuration.Doctor', on_delete=models.DO_NOTHING, null=True, blank=True)
status = models.CharField(
choices=PET_STATUS, max_length=3, null=True, blank=True)
reason = models.CharField(max_length=255)
diagnosis = models.TextField(null=True, blank=True)
treatment = models.TextField(null=True, blank=True)
comment = models.TextField(null=True, blank=True)
prescription = models.TextField(null=True, blank=True)
weight = models.DecimalField(
max_digits=6, decimal_places=2, null=True, blank=True)
class Meta:
ordering = ('-date',)
My issue is that someone using the Django Admin to create a Visit can wrongly choose a Customer and Pet. Hence, the Customer does not own that Pet. I would like to know how can I customise the Django Admin, so that, when the user selects a Customer, only Pets under that particular Customer is displayed in the selectbox.
Below is my admin.py
class VisitAdmin(admin.ModelAdmin):
change_form_template = 'visit/invoice_button.html'
add_form_template = 'visit/add_visit.html'
list_display = ('customer', 'pet', 'date', 'status')
list_filter = ('date', 'customer', 'pet', 'status')
search_fields = ('customer__first_name',
'customer__last_name', 'pet__pet_name')
autocomplete_fields = ['customer', 'pet', 'doctor']
radio_fields = {'status': admin.HORIZONTAL}
fieldsets = (
(None, {
"fields": ('customer', 'pet', 'doctor'),
}),
("Visit Details", {
"fields": ('date', 'reason', 'weight', 'status'),
}),
("Visit Outcome", {
"fields": ('diagnosis', 'treatment', 'comment')
})
)
inlines = [FeeInLine, AttachmentInLine]
actions = ['export_as_csv']
def export_as_csv(self, request, queryset):
meta = self.model._meta
field_names = [field.name for field in meta.fields]
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename={}.csv'.format(
meta)
writer = csv.writer(response)
writer.writerow(field_names)
for obj in queryset:
row = writer.writerow([getattr(obj, field)
for field in field_names])
return response
export_as_csv.short_description = "Export Selected"
def response_change(self, request, obj):
if "invoice" in request.POST:
return render_pdf(request, obj.pk)
return super().response_change(request, obj)
admin.site.register(Visit, VisitAdmin)
i faced the same issue, in my case i use raw_id_field for my foreign keys and many to many fields in ModelAdmin And override add and change template.
you can use raw_id_field for forign keys and in your templates write javascript to change href of search icon for Pet foreign key field when Customer id field changed, and in href use url lookup to show only Pet which belongs to selected Customer
# stock/models.py
class Category(models.Model):
title = models.CharField(max_length=255, blank=True)
is_active = models.BooleanField(default=True)
class Product(models.Model):
category = models.ForeignKey(
Category, on_delete=models.PROTECT, related_name="product"
)
feature_option = models.ManyToManyField("FeatureOption", blank=True, related_name="product")
class Feature(models.Model):
title = models.CharField(max_length=255, blank=True)
category = models.ManyToManyField(Category, blank=True, related_name="feature")
class FeatureOption(models.Model):
title = models.CharField(max_length=255, blank=True)
feature = models.ForeignKey(
Feature, on_delete=models.CASCADE, related_name="feature_option"
)
# stock/admin.py
class CategoryAdmin(admin.ModelAdmin):
raw_id_fields = ['parent']
class ProductAdmin(admin.ModelAdmin):
add_form_template = 'admin/product_add_form.html'
change_form_template = 'admin/product_change_form.html'
raw_id_fields = ['category', "feature_option"]
class FeatureOptionAdmin(admin.ModelAdmin):
list_filter = (("feature__category", admin.RelatedOnlyFieldListFilter),)
and in my template i use javascript to change the href of FeatureOption search icon for url lookup
<!-- product_add_form.html -->
{% extends "admin/change_form.html" %}
{% load i18n %}
{% block admin_change_form_document_ready %} {{ block.super }}
<script lang="text/javascript">
function changeFeatureOptionPopupUrl() {
const featureOptionBaseUrl = "/admin/stock/featureoption/";
let categoryTextBox = document.getElementById("id_category");
document.getElementById("lookup_id_feature_option").href = featureOptionBaseUrl + "?feature__category__id__exact=" + categoryTextBox.value;
}
document.getElementById("lookup_id_feature_option").onfocus = function () {changeFeatureOptionPopupUrl()}
</script>
{% endblock %}

How to categorize content in Django ? Where is the problem with my work?

I wanted to categorize my site content. Show the category titles in the menu and the contents of each category in the body. I have used these codes.
#urls
path('category/<slug:slug>', views.category, name="category")
#views
def category(request, slug):
context = {
"categorys": get_object_or_404(Category, slug=slug, status=True)
}
return render(request, "blog/category.html", context)
#models
class PostManager(models.Manager):
def published(self):
return self.filter(status='p')
class Category(models.Model):
title = models.CharField(max_length=100, verbose_name="عنوان دسته بندی")
slug = models.SlugField(max_length=200, unique=True, verbose_name="آدرس")
status = models.BooleanField(
default=True, verbose_name="آیا نمایش داده شود؟")
position = models.IntegerField(verbose_name="پوزیشن")
class Meta:
verbose_name = "دسته بندی"
verbose_name_plural = "دسته بندی ها"
ordering = ['position']
def __str__(self):
return self.title
class Post(models.Model):
STATUS_CHOICES = [
('d', 'پیش نویس'),
('p', 'منتشر شده'),
]
title = models.CharField(max_length=100, verbose_name="عنوان")
slug = models.SlugField(max_length=200, unique=True, verbose_name="آدرس")
category = models.ManyToManyField(
Category, verbose_name="دسته بندی", related_name="postcat")
description = models.TextField(verbose_name="توضیحات")
thumbnail = models.ImageField(
upload_to="imgpost", height_field=None, width_field=None, max_length=None, verbose_name="تصویر")
publish = models.DateTimeField(
default=timezone.now, verbose_name="زمان انتشار")
created = models.DateTimeField(
auto_now_add=True, verbose_name="زمان ایجاد")
updated = models.DateTimeField(
auto_now=True, verbose_name="زمان بروزرسانی")
status = models.CharField(
max_length=1, choices=STATUS_CHOICES, verbose_name="وضعیت")
def __str__(self):
return self.title
class Meta:
verbose_name = "پست"
verbose_name_plural = "پست ها"
objects = PostManager()
#template
{% for posts in categorys.postcat.published %}
<p>
posts.title
</p>
<p>
posts.description
</p>
{%endfor%}
The problem is that despite the filter I have set for not displaying draft posts, that post is not displayed in the category section. If you can help. my project in my github
{% for posts in categorys.postcat.published %}
<p>
{{ posts.title }}
</p>
<p>
{{ posts.description }}
</p>
{%endfor%}
try this!
We have to put this line of code in the model, the post class. It was a back fever.
objects = PostManager()

Need to know the way to access to my Reply table?

Here is my models.py py file.
from django.db import models
from django.conf import settings
from django.urls import reverse
class Article(models.Model):
'''Modelling the article section.'''
title = models.CharField(max_length=200)
body = models.TextField()
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
date = models.DateTimeField(auto_now_add=True)
def __str__(self):
'''Return string representation of the model.'''
return self.title
def get_absolute_url(self):
'''Return the url of this model.'''
return reverse('article_detail', args=[str(self.id)])
class Comment(models.Model):
'''Modelling the comment section.'''
article = models.ForeignKey(
Article,
on_delete = models.CASCADE,
related_name = 'comments'
)
comment = models.CharField(max_length=150)
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
def __str__(self):
'''String representation of the model. '''
return self.comment
class Reply(models.Model):
''' Modelling the reply section. '''
comment = models.ForeignKey(
Comment,
on_delete = models.CASCADE,
related_name = 'replys'
)
reply = models.CharField(max_length=100)
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE
)
def __str__(self):
''' String representation of the model. '''
return self.reply
I need to access my Reply table in the Detail View template(Using generic view class DetailView). I have tried so far the following command in the template.
article.comments.replys.all
Its not able to retrive any data from Reply table. Thanks in advance.
article.comments is a manager; you need to iterate over it to get Comment instances. Each one will have .replys.
{% for comment in article.comments.all %}
{% for reply in comment.replys.all %}
...
{% endfor %}
{% endfor %}

Django Categories with subcategories

I was able to show categories for Category Model (Man and Woman).
Now I want to make forwarding for a category to the site with subcategory where will show subcategory with product
This is my models.py
class Category(models.Model):
name = models.CharField(max_length=200,
db_index=True)
slug = models.SlugField(max_length=200,
db_index=True)
class Meta:
ordering = ('name',)
verbose_name = 'category'
verbose_name_plural = 'categories'
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('shop:product_list_by_category',
args=[self.slug])
class SubcategoryMan(models.Model):
category = models.ForeignKey(Category,
related_name='subcategorymans')
name = models.CharField(max_length=200,
db_index=True)
slug = models.SlugField(max_length=200,
db_index=True)
def __str__(self):
return self.name
class ProductMan(models.Model):
category = models.ForeignKey(SubcategoryMan,
related_name='productsman')
name = models.CharField(max_length=200,
db_index=True)
slug = models.SlugField(max_length=200,
db_index=True)
image = models.ImageField(upload_to='productsman/%Y/%m/%d',
blank=True)
description = models.TextField(blank=True)
price = models.DecimalField(max_digits=10, decimal_places=2)
stock = models.PositiveIntegerField()
available = models.BooleanField(default=True)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
class Meta:
ordering = ('name',)
index_together = (('id', 'slug'),)
def __str__(self):
return self.name
This is my views.py file
def product_list(request, category_slug=None):
category = None
categories = Category.objects.all()
products = ProductMan.objects.filter(available=True)
if category_slug:
category = get_object_or_404(Category, slug=category_slug)
products = products.filter(category=category)
return render(request,
'shop/product/list.html',
{'category': category,
'categories': categories,
'products': products})
This is my URL (in app shop)
urlpatterns = [
url(r'^$', views.product_list, name='product_list'),
url(r'^(?P<category_slug>[-\w]+)/$',
views.product_list,
name='product_list_by_category')
]
and this is my list.html
{% extends "shop/base.html" %}
{% load static %}
{% block content %}
<ul>
<li>
Wszystkie
</li>
{% for c in categories %}
<li>
{{c.name}}
</li>
{% endfor %}
</ul>
{% endblock %}
I know I must create URL and view for detail, and show product but now I want to know what I can do to create forwarding to the subcategory (create views and URL ? )
I have category Man and Women. I want to when i click on Man forwarding to new site with subcategory ( T-shirt , Shorts etc. )
This is not what happens. Your django application does not actively forward the client somewhere. In fact there is no way the server can send commands to the client. (Okay actually there are ways, but you do not need this in your use case.)
When the client clicks the link you rendered into the previous response a new page will be requested. The information which category was clicked is in the url. Django delegates that request to the correct view depending on the url to render another template. You already defined the url. Now you need a view and a template. The connection between server and client is stateless. That means your django does not know or keep track of which site the client has opened. It just answers to every single request
The django tutorial covers all the code necessary to do exactly this.
A side note:
You do not have not split your categories and products into separate models for each gender. You can just create a model Product with a field gender which enables you to filter every "man category" on the content of this field. Like this:
GENDER_CHOICES = (
('man', 'man'),
('woman', 'woman'),
('kids'), 'kids'), # yeah that's not a gender but you could want to have this
)
class Product(models.Model):
category = models.ForeignKey(Category, related_name='product')
name = models.CharField(max_length=200, db_index=True)
# all the other product fields
gender = models.CharField(max_length=30, choices=GENDER_CHOICES)
If want only the man products in your view you could then filter it like this:
man_products = Product.objects.filter(gender='man')
or this:
# get all men's shoes from the database
man_shoes = Product.object.filter(gender='man', category__slug='shoes')

Categories

Resources