Getting Foreign Key data in Django Admin Add/Change Form - python

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 %}

Related

How to join table if string field is not null

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 }}

Call another model field in url django

My problem is have two models job and company and i want to get all jobs in this company
My urls.py:
url(r'^jobs/(?P<slug>[\w-]+)/$', views.job_at_company, name='job_at_company'),
My views.py:
def job_at_company(request, slug):
return render(request, 'jobs.html')
My models.py:
class Company(models.Model):
title = models.CharField(max_length=100, blank=False)
slug = models.SlugField(blank=True, default='')
city = models.CharField(max_length=100, blank=False)
contact_info = models.CharField(max_length=100, blank=False, default=0)
facebook = models.CharField(max_length=100, blank=True)
twitter = models.CharField(max_length=100, blank=True)
linkedin = models.CharField(max_length=100, blank=True)
logo = models.ImageField(upload_to="logo", default=0)
class Jobs(models.Model):
title = models.CharField(max_length=100)
slug = models.SlugField(blank=True, default='')
company = models.ForeignKey(Company, on_delete=models.CASCADE)
price = models.IntegerField(default='')
Description = models.TextField(blank=True, null=True)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
job_type = models.CharField(max_length=100, choices=(('Full Time', 'Full Time'),('Part Time', 'Part Time')),default='Full Time')
in the views.py we can add this
def job_at_company(request, slug):
results = Jobs.objects.filter(company__slug=slug)
context = {'items':results}
return render(request, 'jobs.html',context)
Suppose you pass id in the url. The id is the primary key of the company. You would have to modify your url to accept id like -
url(r'^jobs/(?P<slug>[\w-]+)/(?P<pk>[\d]+)$', views.job_at_company, name='job_at_company')
And modify your views.py -
def job_at_company(request, slug, pk):
jobs_qs = Jobs.objects.filter(company__id=pk)
return render(request, 'jobs.html', {'jobs': jobs_qs})
And use it in your html like -
{% for job in jobs %}
{{job.title}}
{% endfor %}
Look at this link. Django's documentation is helpful, follow that

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')

Django - Error - Select a valid choice. [<some choice>] is not one of the available choices

views.py
class AddLocationPageView(FormView):
template_name = 'add_location.html'
form_class = LocationForm
success_url = '/add_location/location_added/'
def form_valid(self, form):
form.save()
return super(AddLocationPageView, self).form_valid(form)
models.py
type_choices = (
('Рассвет/Закат', 'Рассвет/Закат'),('Ландшафт', 'Ландшафт'),('Природа', 'Природа'),
('Вода', 'Вода'),('Животные', 'Животные'),('Люди', 'Люди'),
('Архитектура', 'Архитектура'),('Город', 'Город'),('Астрофото', 'Астрофото'),
('Панорама', 'Панорама'),('Транспорт', 'Транспорт'),('Свадьба', 'Свадьба'),
)
visit_choices = (
('Январь', 'Январь'),('Февраль', 'Февраль'),('Март', 'Март'),
('Апрель', 'Апрель'),('Май', 'Май'),('Июнь', 'Июнь'),
('Июль', 'Июль'),('Август', 'Август'),('Сентябрь', 'Сентябрь'),
('Октябрь', 'Октябрь'),('Ноябрь', 'Ноябрь'),('Декабрь', 'Декабрь'),
)
class Location(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, default=1)
name = models.CharField(max_length=100, verbose_name="Локация", default='')
types = models.CharField(max_length=50, verbose_name="Тип локации", choices=type_choices, default='')
visit_times = models.CharField(max_length=50, verbose_name="Лучшее время для съемки", choices=visit_choices, default='')
photos = models.ImageField(upload_to='photos', null=True, blank=True)
keywords = models.CharField(max_length=100, verbose_name="Ключевые слова", default='')
description = models.TextField(null=True, blank=True)
def __unicode__(self):
return self.name
forms.py
class LocationForm(forms.ModelForm):
class Meta:
model = Location
fields = ['name', 'types', 'visit_times', 'photos', 'keywords', 'description']
widgets = {
'name': forms.TextInput(attrs={'placeholder': 'Напр. Стоунхендж'}),
'types': forms.SelectMultiple(),
'visit_times': forms.SelectMultiple(),
'keywords': forms.TextInput(attrs={'placeholder': 'Напр. море, побережье, скалы'}),
'description': forms.Textarea(attrs={'placeholder': 'Любая информация, которую посчитаете нужной'})
}
I fill in the fields on the page and do some choice on SELECT fields. Then I push the button and get the error on SELECT fields --> Select a valid choice. [some choice] is not one of the available choices
Thanks a lot!
Try
'types': forms.Select(),
'visit_times': forms.Select(),
You defined in your model that types and also visit_times can have only one value from the choices. It is possible to get more choices but you have to think about how to save this data to database. Take a look at this:
https://pypi.python.org/pypi/django-multiselectfield/
https://pypi.python.org/pypi/django-select-multiple-field/

How to join results of two models in Django?

I am trying to make a user panel in which each user's profile info (like avatar, joined date, etc.) are being displayed along with their posts. Here is the view that render the threads:
def topic(request, topic_id):
"""Listing of posts in a thread."""
posts = Post.objects.select_related('creator') \
.filter(topic=topic_id).order_by("created")
posts = mk_paginator(request, posts, DJANGO_SIMPLE_FORUM_REPLIES_PER_PAGE)
topic = Topic.objects.get(pk=topic_id)
topic.visits += 1
topic.save()
return render_to_response("myforum/topic.html", add_csrf(request, posts=posts, pk=topic_id,
topic=topic), context_instance=RequestContext(request))
The Topic model is:
class Topic(models.Model):
title = models.CharField(max_length=100)
description = models.TextField(max_length=10000, null=True)
forum = models.ForeignKey(Forum)
created = models.DateTimeField()
creator = models.ForeignKey(User, blank=True, null=True)
visits = models.IntegerField(default = 0)
And the UserProfile model:
class UserProfile(models.Model):
username = models.OneToOneField(User)
name = models.CharField(max_length=30, blank=True)
city = models.CharField(max_length=30, blank=True)
country = models.CharField(
max_length=20, choices= COUTNRY_CHOICES, blank=True)
avatar = ImageWithThumbsField(), upload_to='images', sizes=((32,32),(150,150),(200,200)), blank=True)
created_at = models.DateTimeField(auto_now_add=True, blank=True)
updated_at = models.DateTimeField(auto_now=True, blank=True)
The problem is how best to join these two tables so that userprofile fields can be displayed in topic.html along with username?
Add them to context since you already have a database relation Users and Topics.
# add this to context
topic = Topic.objects.get(pk=topic_id)
creator = topic.creator.get().profile # This pulls the user object from creator field
context['creator'] = creator # Add to context
Now you can use the 'creator' context to pull fields
<h1>{{ creator.name }}</h1>
as for the avatar, if you have your media root set in settings you simply use an
<img src="/media/images/{{ creator.avatar }}">
Oh and also you can save alot of time by using ListView and DetailView part of Django's class based views.
Sorry forgot to mention you should add a related name to your one to one,
username = OneToOneField(User, related_name='profile')

Categories

Resources