Django very large reverse ForeignKey query - python

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

django-elasticsearch-dsl does not return all records that match the query condition

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

How to count the number of related entities over two m2m relations to the same Model in Django

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

django admin - matching query does not exist

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

Openerp: Iterate Many2Many field

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.

Django/ python-social-auth: LinkedIn extra data returns null on some fields

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

Categories

Resources