I have Pick model which when I select a record within Django Admin takes an age (about 20 seconds) to retrieve the record (there are about 70k). However, it is quick when I try and create/save a record. I have added the indexes to try and speed up retrieval but don't really know what I should be doing
class Pick (models.Model):
team = models.ForeignKey(Team, on_delete=models.CASCADE, null=True, blank=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, db_index=True)
contest_entry=models.ForeignKey(ContestEntry, on_delete=models.CASCADE, db_index=True)
game_round=models.ForeignKey(GameRound, on_delete=models.CASCADE, db_index=True)
objects = DataFrameManager()
def __str__(self):
return f'%s %s' % (self.contest_entry, self.game_round)
class Meta:
unique_together = ['user', 'contest_entry', 'game_round']
ordering = ['contest_entry','game_round','user','team']
index_together = [["user", "contest_entry", 'game_round'],]
indexes = [
models.Index(fields=['user', 'contest_entry', 'game_round']),
models.Index(fields=['game_round'], name='game_round_idx'),
models.Index(fields=['contest_entry'], name='contest_entry_idx'),
]
class PickAdmin(admin.ModelAdmin):
model = Pick
list_filter= (
('user', RelatedDropdownFilter),
('contest_entry__contest', RelatedDropdownFilter),
('game_round', RelatedDropdownFilter)
)
def get_queryset(self, request):
return super().get_queryset(request).select_related('user','contest_entry','game_round')
admin.site.register(Pick, PickAdmin)
How can I improve performance here?
The key is specifying the raw_id_fields on the admin class. The default <select> widget for foreign key relationships can cause a lot of overhead retrieving and rendering all the options.
class PickAdmin(admin.ModelAdmin):
model = Pick
list_filter= (
('user', RelatedDropdownFilter),
('contest_entry__contest', RelatedDropdownFilter),
('game_round', RelatedDropdownFilter)
)
raw_id_fields = ('user', 'contest_entry', 'game_round',)
def get_queryset(self, request):
return super().get_queryset(request).select_related('user','contest_entry','game_round')
My guess is that what takes so long is rendering the dropdowns for the ForeignKey fields of your model.
I'd suggest looking at the SQL queries that are performed, either by installing Django toolbar or enabling logging in your database server.
You can improve the queries by using select_related for foreignkeys https://docs.djangoproject.com/en/2.2/ref/models/querysets/#select-related
Related
I currently have a SiteViewSet using ModelViewSet that can list, retrieve, create, and update sites from the model Site.
I want to add a filter to the same SiteViewSet that lists all the site objects where is_active is True - but this condition is based on the AdminConfiguration model.
I was not able to find how to implement a filter in ModelViewSet based on conditions of another model. Is the filtered endpoint /v1/site/?is_active=True possible to implement in the same SiteViewSet or should I create a separate view to achieve this via the more flexible APIView?
SiteViewSet:
class SiteViewSet(viewsets.ModelViewSet):
model = Site
lookup_field = 'uuid'
pagination_class = PageNumberPagination
serializer_class = SiteSerializer
queryset = Site.objects.all()
Site Model:
class Site(models.Model):
uuid = models.UUIDField(
default=uuid.uuid4,
unique=True)
domain_name = models.CharField(max_length=255, unique=True)
AdminConfiguration Model:
class AdminConfiguration(models.Model):
site = models.OneToOneField(
Site,
on_delete=models.CASCADE)
always_active = models.BooleanField(default=False)
subscription_ends = models.DateTimeField(
default=set_default_expiration)
#property
def is_active(self):
active_status = self.always_active or self.subscription_ends > timezone.now()
return active_status
In DRF filter based on another model is the same that filter against a model field. And you achieve this by override the get_queryset method like this :
we assume that you have declared your OneToOneField like :
site = models.OneToOneField(Site, on_delete=models.CASCADE, related_name='admin_config').
def get_queryset(self):
queryset = Site.objects.filter(admin_config__is_active=True)
.filter(related_model_name__field=value)
NB : Your is_active field is a property ! So i have never tested with #property field of a model. But it work fine with standard fields.
Don't also forget to remove the queryset attribute if you override get_queryset method. One of them, not both.
I just started learning Django this week and I'm trying to figure out how I can remove an option from a select menu being rendered in a class based view. The dropdown is for a Foreign Key field that links to my users table.
The functionality here is that I do not want the current user logged into show up on that list (basically I don't want someone to be able to select themself). How can I go about doing this?
View:
class TransferCreateView(CreateView):
model = Transfer
template_name = 'points/transfer_form.html'
fields = ['receiver', 'message', 'amount']
Model:
class Transfer(models.Model):
receiver = models.ForeignKey(User, null=False,
on_delete=models.CASCADE, related_name='receiver')
sender = models.ForeignKey(User, null=False, on_delete=models.CASCADE, related_name='sender')
amount = models.IntegerField(
validators=[
MinValueValidator(1),
MaxValueValidator(1000)],
null=False)
message = models.CharField(max_length=100)
date_sent = models.DateTimeField(default=timezone.now)
Basically, I don't want the person who is the sender (which i was going to set in the code in a form_valid() function) to be an option for 'receiver' in the template when it renders.
Override the get_form method of the CreateView and change the queryset of that field, something like this:
class TransferCreateView(CreateView):
model = Transfer
template_name = 'points/transfer_form.html'
fields = ['receiver', 'message', 'amount']
def get_form(self, form_class):
form = super().get_form(form_class)
form.fields['receiver'].queryset = User.objects.exclude(id=self.request.user.id)
return form
I need some help doing a join using Django, which seems like it should be easy. I have looked at the documentation but it seems like it won't join for some reason.
I am trying to get in my view, the model.Photo and model.PhotoExtended with both joined and then displayed in the view. Currently I am just trying to get the model.Photo displayed but with a join which finds the request.user and filters it based on that.
They are in different apps.
models.py for model.Photo
class Photo(ImageModel):
title = models.CharField(_('title'),
max_length=60,
unique=True)
slug = models.SlugField(_('slug'),
unique=True,
help_text=_('A "slug" is a unique URL-friendly title for an object.'))
models.py for model.PhotoExtended
class PhotoExtended(models.Model):
Photo = models.OneToOneField(Photo, related_name='extended', help_text='Photo required', null=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, help_text='User that uploaded the photo')
views.py
class PhotoExtendedUserView(ListView):
template_name = 'photo_user_list.html'
def get_queryset(self):
user = get_object_or_404(User, username=self.request.user)
return Photo.objects.filter(photoextended__user=user)
You set the related_name on Photo (which shouldn't be capitalized by the way) to extended so you need to filter like so:
class PhotoExtendedUserView(ListView):
template_name = 'photo_user_list.html'
def get_queryset(self):
user = get_object_or_404(User, username=self.request.user)
# 'extended' vs. 'photoextended'
return Photo.objects.filter(extended__user=user)
Problem Statement:
I am using Django admin to manage many tables, some of which have many-to-many relationships. I am unable to save new records in tables (models) that have manytomany fields defined. I am able to render the add form just fine. The problem is only upon trying to save the record. I do not have the same problem when updating an existing record.
Using the models below, I receive the following error: 'Bout' instance needs to have a primary key value before a many-to-many relationship can be used.
The Bout model has a many-to-many relationship with the Equipment model. The BoutEquipment model is the intermediate model.
I've researched this problem high and low on StackOverflow and via Google, but am thus far unable to find a solution.
Disclosure: I'm new to Django and new-ish to Python. I'm hopeful that there's a relatively simple solution to this problem.
Thanks in advance.
models.py
class Bout(models.Model):
boutid = models.AutoField(db_column=u'BoutID', primary_key=True)
sessionid = models.ForeignKey(Session, db_column=u'SessionID', verbose_name=u'Session')
activitytypeid = models.ForeignKey(Activitytype, db_column=u'ActivityTypeID', verbose_name=u'Activity Type')
locationid = models.ForeignKey(Location, db_column=u'LocationID',verbose_name=u'Location')
equipment = models.ManyToManyField(Equipment, verbose_name=u'Related Equipment', related_name=u'Bout_Equipment', blank=True, null=True) #through = 'BoutEquipment'
intensitymetrics = models.ManyToManyField(Intensitymetric, verbose_name=u'Related Intensity Metrics', related_name=u'Bout_IntensityMetrics', blank=True, null=True) #through = 'BoutIntensitymetric'
def __unicode__(self):
return u'%s %s' % (self.sessionid, self.activitytypeid)
class Meta:
db_table = u'app_bout'
verbose_name = u'Bout'
verbose_name_plural = u'Bouts'
class Equipment(models.Model):
equipmentid = models.AutoField(db_column=u'EquipmentID', primary_key=True)
name = models.CharField("Name", max_length=100, db_column=u'Name')
equipmenttypeid = models.ForeignKey(Equipmenttype, db_column=u'EquipmentTypeID', verbose_name = u'Equipment Type')
def __unicode__(self):
return self.name
class Meta:
db_table = u'app_equipment'
verbose_name = u'Equipment'
verbose_name_plural = u'Equipment'
class BoutEquipment(models.Model):
id = models.AutoField(db_column=u'id', primary_key=True)
boutid = models.ForeignKey(Bout, db_column=u'Bout_ID')
equipmentid = models.ForeignKey(Equipment, db_column=u'Equipment_ID')
def __unicode__(self):
return self.name
class Meta:
db_table = u'app_bout_equipments'
admin.py
class EquipmentAdmin(admin.ModelAdmin):
form = EquipmentForm
inlines = [EquipmentShoeInline, EquipmentLogInline]
list_display = ('name','equipmenttypeid','manufacturer','retired','retiredby','retiredon','notes')
fields = (
'name',
('equipmenttypeid','manufacturer'),
('retired','retiredby','retiredon'),
'notes'
)
class BoutAdmin(admin.ModelAdmin):
form = BoutForm
filter_horizontal = ('equipment','intensitymetrics',)
list_display = ('sessionid','activitytypeid','locationid','sequence','activehand','baddata')
inlines = [BoutDeviceInline,]
fields = (
('sessionid','locationid','activitytypeid'),
'videofilelocation',
'sequence',
'activehand',
'notes',
'baddata',
('equipment','intensitymetrics')
)
A manytomany field in django is a join table between the two models you want to connect to each other.
This happens on SQL level, so both models have to exist in the database.
bout = Bout()
...
equipment = Equipment()
...
bout.equipment.add(equipment)
#fails because bout and equipment are not saved
bout.save()
bout.equipment.add(equipment)
#fails because equipment is not saved
equipment.save()
bout.equipment.add(equipment)
#yay :)
I have a Competition model which has a corresponding CompetitionEntry model. I'd like to show the number of entries for each competition in the admin view.
Here's the model definition:
class Competition(models.Model):
def __unicode__(self):
return self.competition_name
competition_name = models.CharField(max_length=100)
competition_text = models.TextField()
active = models.BooleanField('Is this competition active?', blank=True)
date_posted = models.DateTimeField(auto_now_add=True)
class CompetitionEntry(models.Model):
def __unicode__(self):
return self.competition.competition_name
competition = models.ForeignKey(Competition)
user = models.ForeignKey(User)
date_entered = models.DateTimeField(auto_now_add=True)
is_winner = models.BooleanField('Is this entry the winner?', blank=True)
My Django skills are a little rusty, but there should be a fairly simple way to add this to the admin, right? Any pointers? I can't quite work out how the Competition class can 'talk' to the CompetitionEntry class since the relationship is defined inside CompetitionEntry, but I want to show the entries inside Competition.
You can refer to python functions in a ModelAdmin by adding it to the fieldsets or list_display and readonly_fields attributes.
You can 'talk' to a reverse relationship via the reverse related managers dynamically added to each class that a foreign key points to which is, by default, lowercasemodelname_set and behaves exactly like your default objects manager.
class MyAdmin(admin.ModelAdmin):
list_display = ('_competition_count',)
readonly_fields = ('_competition_count',)
fieldsets = (
(None, {'fields': (
'_competition_count',
)})
)
def _competition_count(self, obj):
return obj.competitionentry_set.count()
_competition_count.short_description = "Competition Count"