I am using Django admin to filter objects on their reverse ForeignKey objects field value. The App has a ForeignKey to Contact and I am trying to filter Contacts by Apps category in Django admin. The problem is that the query is very large and I am getting a timeout error. There are around 300k Contact objects and around 1M of Apps. There are 500 results per page in Django admin. The db indexes and prefetch_related are added. What else should I do to optimise Django admin ? I am using sqlite db.
The code:
class App(models.Model):
contact = models.ForeignKey('Contact', related_name='apps', null=True)
category = models.TextField(blank=True, null=True, db_index=True)
store = models.IntegerField(choices=STORE_TYPES, db_index=True)
class Meta:
index_together = [
['category', 'store'],
]
# admin:
class ContactAdmin(admin.ModelAdmin):
list_filter = (
AppCategory,
)
def queryset(self, request):
return super(ContactAdmin, self).queryset(request).prefetch_related(
'apps',
'to_contact',
)
# the main list_filter that is causing troubles:
class AppCategory(admin.SimpleListFilter):
title = 'app category'
parameter_name = 'app_category'
def lookups(self, request, modelAdmin):
return [
('Action', 'Action'),
('Adventure', 'Adventure'),
('Arcade', 'Arcade'),
('Board', 'Board'),
('Books', 'Books'),
('Books & Reference', 'Books & Reference'),
('Business', 'Business'),
('Card', 'Card'),
('Casino', 'Casino',),
('Casual', 'Casual'),
('Catalogs', 'Catalogs'),
('Comics', 'Comics'),
('Communication', 'Communication'),
('Education', 'Education'),
('Educational', 'Educational'),
('Entertainment', 'Entertainment'),
('Family', 'Family'),
('Finance', 'Finance'),
('Food & Drink', 'Food & Drink'),
('Games', 'Games'),
('Health & Fitness', 'Health & Fitness'),
('Libraries & Demo', 'Libraries & Demo'),
('Lifestyle', 'Lifestyle'),
('Media & Video', 'Media & Video'),
('Medical', 'Medical'),
('Music', 'Music'),
('Music & Audio', 'Music & Audio'),
('Navigation', 'Navigation'),
('News', 'News'),
('News & Magazines', 'News & Magazines'),
('Personalization', 'Personalization'),
('Photo & Video', 'Photo & Video'),
('Photography', 'Photography'),
('Productivity', 'Productivity'),
('Puzzle', 'Puzzle'),
('Racing', 'Racing'),
('Reference', 'Reference'),
('Role Playing', 'Role Playing'),
('Shopping', 'Shopping'),
('Simulation', 'Simulation'),
('Social', 'Social'),
('Social Networking', 'Social Networking'),
('Sports', 'Sports'),
('Strategy', 'Strategy'),
('Tools', 'Tools'),
('Transportation', 'Transportation'),
('Travel', 'Travel'),
('Travel & Local', 'Travel & Local'),
('Trivia', 'Trivia'),
('Utilities', 'Utilities'),
('Weather', 'Weather'),
('Word', 'Word')
]
def queryset(self, request, queryset):
if not self.value():
return queryset
else:
qs = queryset.filter(
apps__category=self.value()
)
return qs
I found out the answer. If you are filtering on one reverse ForeignKey relationship (apps__category in this case) everything works fine. But I forgot to mention that I am combining two filters at the right side of Django admin page - the other one is also a reverse ForeignKey - apps__store. When you are doing that, two completely same INNER JOINs are performed and this results in a server timeout. When I am using these filters separately, everything works fine.
See: https://code.djangoproject.com/ticket/16554
Related
I'm writing a site on Django. I'm developing a site search system using django-elasticsearch-dsl. But I noticed a problem that not all records that meet the search condition are displayed.
For example, I know that I have 6 books in my database that contain the word 'Python', but when I use the command
books = BookDocument.search().query('match', title='Python')
django-elasticsearch-dsl gives me only 3 entries
books = BookDocument.search().query('match', title='Python')
for i in books:
print(i.title)
|||||||||||||
Укус Python
Програмуємо на Python
Django:Практика створення Web-сайтів на Python
And the remaining 3 are not displayed
Here is my BookDocument class:
#registry.register_document
class BookDocument(Document):
"""
Індексація таблиці БД Book для Elasticsearch-пошуку
Згідно із документацією django-elasticsearch-dsl
https://django-elasticsearch-dsl.readthedocs.io/en/latest/fields.html
"""
image = FileField()
category = ObjectField(properties={
'title': TextField(),
'pk': IntegerField()
})
authors = NestedField(properties={
'pk': IntegerField(),
'name': TextField()
})
class Index:
name = 'book'
settings = {
'number_of_shards': 1,
'number_of_replicas': 0
}
class Django:
model = Book
fields = [
'id',
'title',
'description',
]
related_models = [Category, Author]
def get_instances_from_related(self, related_instance):
if isinstance(related_instance, Category):
return related_instance.book_set.all()
if isinstance(related_instance, Author):
return related_instance.book_set.all()
Maybe someone has already encountered this and knows how to fix it
Given roughly these two models:
class Person(models.Model):
name = models.CharField()
class Resource(models.Model):
people_contributing = models.ManyToManyField(
Person,
related_name='resouces_contributing_to'
)
people_involved = models.ManyToManyField(
Person,
related_name='resources_involved_in'
)
For all persons I want to get the count of resources he/she is either contributing to OR involved in.
I tried the following:
resources = Resource.objects.all()
participations = Person.objects.filter(
Q(resources_contributing_to__in=resources) |
Q(resources_involved_in__in=resources)
).values(
# group results by person
'pk'
).annotate(
count=Count('id')
).values_list(
'pk',
'name',
'count'
).order_by('-count')
print(participations)
This gives me a list of tuples like this:
[
# (id, name, count)
(1, 'John Doe', 12),
(2, 'Jane Doe', 5),
(3, 'Donald Trump', 3),
]
However if a person is both contributing and involved the resource will be counted twice because the resource will be joined twice to the person table. What I want is the resource to be counted only once if it is present in both relationships.
How do I have to change my queryset to prevent this?
I am using postgresql and Django 1.11.
Counting entries that are appearing in either of relations can be achieved by counting entries from 1st relation + counting entries from 2nd relation - counting entries from both relations. That can be achieved in Django by this queryset:
participations = Person.objects.filter(
Q(resources_contributing_to__in=resources) |
Q(resources_involved_in__in=resources)
).annotate(
count=Count('resouces_contributing_to__id', distinct=True) + Count('resources_involved_in__id', distinct=True) - Count(Case(When(resources_involved_in__id=F('resouces_contributing_to__id'), then='resources_involved_in__id')), distinct=True),
).values_list(
'pk',
'name',
'count'
).order_by('-count')
Edit
I think its something related to django-fsm, have a look at this code I wrote for States
class STATE:
SUBMITTED = 'Submitted'
VERIFIED_BY_DA = 'Verified by DA'
APPROVED_BY_MS = 'Verified by MS'
APPROVED_BY_DR = 'Approved by DR'
APPROVED_BY_SrAO = 'Approved by SAO.'
APPROVED_BY_R = 'Approved by R'
AMOUNT_TRANSFERRED = 'Accepted'
REJECT = 'Rejected'
def __init__(self):
pass
STATE_CHOICES = (
(STATE.SUBMITTED, 'Submitted', 'Medical'),
(STATE.VERIFIED_BY_DA, 'Verified by DA', 'Medical'),
(STATE.APPROVED_BY_MS, 'Approved by MD', 'Medical'),
(STATE.APPROVED_BY_DR, 'Approved by DR', 'Medical'),
(STATE.APPROVED_BY_SrAO, 'Approved by SAO', 'Medical'),
(STATE.APPROVED_BY_R, 'Approved by R', 'Medical'),
(STATE.AMOUNT_TRANSFERRED, 'Amount transferred by AD', 'Medical'),
(STATE.REJECT, 'Reject', 'Medical'),
)
So I have these two models defined in separate file under models folder in django
First I defined only Medical model and everything was working fine
from django_fsm import FSMField
from state import STATE
from state import STATE_CHOICES
class Medical(BaseModel):
general_detail = models.ForeignKey(
GeneralDetail,
help_text='General Detail'
)
state = FSMField(
blank=True,
protected=not settings.DEBUG,
default=STATE.SUBMITTED,
state_choices=STATE_CHOICES,
)
def __str__(self):
return str(self.general_detail.employee.user.first_name) + ' ' \
+ str(self.general_detail.employee.user.last_name)
def __unicode__(self):
return str(self.general_detail.employee.user.first_name) + ' ' \
+ str(self.general_detail.employee.user.last_name)
But just after adding this below model it gives error in django-admin when saving a field in transition history using django-admin.
class TransitionHistory(BaseModel):
state_from = FSMField(
blank=True,
protected=not settings.DEBUG,
default=STATE.SUBMITTED,
state_choices=STATE_CHOICES,
)
state_to = FSMField(
blank=True,
protected=not settings.DEBUG,
default=STATE.SUBMITTED,
state_choices=STATE_CHOICES,
)
def __str__(self):
return str(self.state_from) + str(self.state_to)
def __unicode__(self):
return str(self.state_from) + str(self.state_to)
Error
Exception Type: DoesNotExist
Exception Value: Medical matching query does not exist
Line 379
C:\Python27\lib\site-packages\django\db\models\query.py in get
self.model._meta.object_name
Just check if you have made migrations, if yes then there is a possibility that you might have forgotten registering your model in the admin.py
Here's my class:
employee_ids = fields.Many2many('hr.employee', string="Empls")
status = fields.Selection([
('draft', 'Draft'),
('done', 'Done'),
])
then in fields_view_get method i want to iterate through employee_ids and make list of each employee.
def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
if context is None:
context = {}
res = super(help_desk, self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar,submenu=False)
#here i want to iterate throught employee_ids and make list of each employee
for f in res['fields']:
if f == 'status':
res['fields'][f]['selection'] = #put list here
return res
how can i do it? thanks
I am supposing help_desk model have 3 fields Selection,Many2one and Many2many:
status = fields.Selection([
('draft', 'Draft'),
('done', 'Done'),
])
partner_id = fields.Many2one(comodel_name='res.partner', string='Partner')
employee_ids = fields.Many2many('hr.employee', string="Empls")
Now if you want to apply some logic so go through the below mention lines.
Well method like fields_get ,fields_view_get help us in improving UI experience by applying the filter/domain on fly.
so the code is here:
#api.model
def fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False):
res = super(help_desk, self).fields_view_get(view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu)
PartnerObj= self.env['res.partner']
domain = [('phone','!=',False)]# put your domain or just place blank list
partners = PartnerObj.search(domain)
if partners
for field in res['fields']:
# if field == 'partner_id':
# res['fields'][field]['domain'] = [('id', 'in', partners.ids)]
elif field == 'employee_ids':
res['fields'][field]['domain'] = [('id', 'in', partners.ids)]
elif field=='status':
# Appending the partners in status doesn't making any sense but as per your words "put list here"
res['fields'][field]['selection'] = partners and [(partner.id, partner.name) for partner in partners] or [('', '')]
return res
here i have put the domain on Many2one and Many2many and add some dynamic list based on domain inside the Selection field.
you can also refer account/models/chart_template.py.
I am using python-social-auth to retrieve LinkedIn profile data in Django.
Login with LinkedIn
is the link to login with LinkedIn.
SOCIAL_AUTH_LINKEDIN_OAUTH2_EXTRA_DATA = [('id', 'id'), ('firstName', 'first_name'), ('lastName', 'last_name'), ('email-address', 'email_address'), ('positions', 'positions'), ('summary', 'summary'), ('headline', 'headline'), ('picture-url', 'picture_url'),
('site-standard-profile-request', 'site_standard_profile_request'), ('public-profile-url', 'public_profile_url'), ('location', 'location'), ('interests', 'interests'), ('skills', 'skills'), ('languages', 'languages'),]
However, when I check the extra data, it returns only the first name, last name, access token and id correctly. Everything else is null.
What am I missing?
The settings to obtain the extra data must be in the following format:
SOCIAL_AUTH_LINKEDIN_FIELD_SELECTORS = [
'public-profile-url',
'email-address',
'interests',
'skills',
]
SOCIAL_AUTH_LINKEDIN_SCOPE = ['r_basicprofile', 'r_emailaddress']