django "exclusive" checkbox - django

I was wondering about how could I have in Django an "exclusive" Checkbox and set it in the admin? What I mean is that I have an app that stores polls and there can only be one in the frontpage, so I add a front_page field to the DB and make it bool. True show, False don't. So whenever I set in the admin a poll's front_page field to true I want every other poll to be set to false, this editing being done in the admin. As a Flasker I can think of 100 ways of doing it elsewhere, but this django thing has got me confused. Thanks!

Basically what you want to do is override the save method of the model that has the exclusive flag, and then in the save method check to see if the flag is turned on. If it is, then do a query to find all the records that have the flag turned on and turn them off. Then save. It would look something like this:
class Poll(models.Model):
#...
front_page = models.BooleanField()
#...
def save(self, *args, **kwargs):
if self.front_page:
Poll.objects.filter(front_page=True).update(front_page=False)
super(Poll, self).save(*args, **kwargs)
Hope this helps!

Related

django - limit the number of users

(sorry for my english)
Only a question, exist any way to limit the number of users that can be created in a Django App?
I search in a lot of places and i only find this, but i see in the repo that the last update was 3 years ago https://github.com/1stvamp/django-limit-users
I don't know if exist any way in the core of django or if i have to override something!
Thanks very much!
While I don't have time to test https://github.com/1stvamp/django-limit-users against a new Django, it goes in the right direction using django's signals: https://docs.djangoproject.com/en/dev/ref/signals/
So for example, you could write a pre_save or a post_save handler and connect it to the signals emitted before / after saving your user model.
A simple post_save handler could look like:
def user_post_save(sender, instance, created, **kwargs):
if created and sender.objects.count() > MY_LIMIT:
instance.is_active = False
instance.save()
A simple pre_save handler would look like:
def user_pre_save(sender, instance, **kwargs):
if instance.id is None and sender.objects.count() > MY_LIMIT:
instance.is_active = False # Make sure the user isn't active
Instead of the last line in the pre_save handler, you could also raise an Exception to make sure the User isn't even saved to the DB.
Another option would be to combine this with a custom user model so instead of is_active you could use over_limit or whatever you want. The repo you linked is achieving that with a separate DisabledUser model.

How do I change the "no-selection" text of a Django-admin Select field?

When the Django Admin uses the Select widget, the default/top/no-selection option is "--------":
Is there a way to customize that option? I tried manually modifying the "choices" value of the Select field:
def __init__(self, *args, **kwargs):
super(MyAdminForm, self).__init__(*args, **kwargs)
self.fields['my_field'].choices[0] = (u'', 'something else')
but that doesn't work, as the admin still displays "--------"...
Update:
The answers in the suggested duplicate question almost solve my problem... however, the answers there seem to be techniques for giving ALL of the Select fields the same no-selection option.
In my case, the ModelForm in question is used by a TabularInline ModelAdmin, so the form is repeated 20 times on the page, and different instances need a different no-selection value (that is dependent on self.instance).
Also, the most up-voted answer doesn't work with the latest version of Django - the empty_label field now needs to be set on the self.base_fields['my_field'] object before calling super(). But I need self.instance to exist in order to do my customizing, and self.instance is None before super() is called...

Django model.save() not working with loaddata

I have a model which is overriding save() to slugify a field:
class MyModel(models.Model):
name = models.CharField(max_length=200)
slug = models.SlugField(max_length=200)
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super(MyModel, self).save(*args, **kwargs)
When I run load data to load a fixture, this save() does not appear to be called because the slug field is empty in the database. Am I missing something?
I can get it to work by a pre_save hook signal, but this is a bit of a hack and it would be nice to get save() working.
def mymodel_pre_save(sender, **kwargs):
instance = kwargs['instance']
instance.slug = slugify(instance.name)
pre_save.connect(mymodel_pre_save, sender=MyModel)
Thanks in advance.
No you're not. save() is NOT called by loaddata, by design (its way more resource intensive, I suppose). Sorry.
EDIT: According to the docs, pre-save is not called either (even though apparently it is?).
Data is saved to the database as-is, according to https://docs.djangoproject.com/en/dev/ref/django-admin/#what-s-a-fixture
I'm doing something similar now - I need a second model to have a parallel entry for each of the first model in the fixture. The second model can be enabled/disabled, and has to retain that value across loaddata calls. Unfortunately, having a field with a default value (and leaving that field out of the fixture) doesn't seem to work - it gets reset to the default value when the fixture is loaded (The two models could have been combined otherwise).
So I'm on Django 1.4, and this is what I've found so far:
You're correct that save() is not called. There's a special DeserializedObject that does the insertion, by calling save_base() on the Model class - overriding save_base() on your model won't do anything since it's bypassed anyway.
#Dave is also correct: the current docs still say the pre-save signal is not called, but it is. It's behind a condition: if origin and not meta.auto_created
origin is the class for the model being saved, so I don't see why it would ever be falsy.
meta.auto_created has been False so far with everything I've tried, so I'm not yet sure what it's for. Looking at the Options object, it seems to have something to do with abstract models.
So yes, the pre_save signal is indeed being sent.
Further down, there's a post_save signal behind the same condition that is also being sent.
Using the post_save signal works. My models are more complex, including a ManyToMany on the "Enabled" model, but basically I'm using it like this:
from django.db.models.signals import post_save
class Info(models.Model):
name = models.TextField()
class Enabled(models.Model):
info = models.ForeignKey(Info)
def create_enabled(sender, instance, *args, **kwards):
if Info == sender:
Enabled.objects.get_or_create(id=instance.id, info=instance)
post_save.connect(create_enabled)
And of course, initial_data.json only defines instances of Info.

How to force Django Admin to use select_related?

One of my models is particularily complex. When I try to edit it in Django Admin it performs 1042 queries and takes over 9 seconds to process.
I know I can replace a few of the drop-downs with raw_id_fields, but I think the bigger bottleneck is that it's not performing a select_related() as it should.
Can I get the admin site to do this?
you can try this
class Foo(admin.ModelAdmin):
list_select_related = (
'foreign_key1',
'foreign_key2',
)
https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_select_related
Although dr jimbob's answer makes sense, for my needs, I was able to simply override the get_queryset() method with a one-liner, even selecting a foreign key's foreign key. Maybe this could be helpful to someone.
class MyModelAdmin(admin.ModelAdmin):
model = MyModel
...
def get_queryset(self, request):
return super(MyModelAdmin, self).get_queryset(request).select_related(
'foreign_key1', 'foreign_key2__fk2_foreign_key')
For my particular model, the particularly slow aspect is going through ForeignKeys when they were being displayed in forms, which aren't called using select_related, so that's the part I'm going to speed up.
Looking through the relevant django source, you see in django/contrib/admin/options.py that the method formfield_for_foreignkeys takes each FK db_field and calls the ForeignKey class's formfield method, which is defined in django/db/models/fields/related/ like:
def formfield(self, **kwargs):
db = kwargs.pop('using', None)
defaults = {
'form_class': forms.ModelChoiceField,
'queryset': self.rel.to._default_manager.using(db).complex_filter(self.rel.limit_choices_to),
'to_field_name': self.rel.field_name,
}
defaults.update(kwargs)
return super(ForeignKey, self).formfield(**defaults)
From this, we see if we provide the db_field with a kwargs['queryset'] we can define a custom queryset that will be use select_related (this can be provided by formfield_for_foreignkey).
So basically what we want to do is override admin.ModelAdmin with SelectRelatedModelAdmin and then make our ModelAdmin subclasses of SelectRelatedModelAdmin instead of admin.ModelAdmin
class SelectRelatedModelAdmin(admin.ModelAdmin):
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if 'queryset' in kwargs:
kwargs['queryset'] = kwargs['queryset'].select_related()
else:
db = kwargs.pop('using', None)
kwargs['queryset'] = db_field.rel.to._default_manager.using(db).complex_filter(db_field.rel.limit_choices_to).select_related()
return super(SelectRelatedModelAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
This code sample doesn't cover admin Inlines or ManyToManyFields, or foreign_key traversal in functions called by readonly_fields or custom select_related queries, but a similar approach should work for those cases.
In Django 2.0+, a good way to improve performance of ForeignKey and ManyToMany relationships is to use autocomplete fields.
These fields don't show all related objects and therefore load with many fewer queries.
For the admin edit/change a specific item page, foreign key select boxes may take a long time to load, to alter the way django queries the data for the foreign key:
Django docs on Using formfield_for_foreignkey
Say I have a field called foo on my Example model, and I wish to select ralated bar objects:
class ExampleAdmin(admin.ModelAdmin):
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == "foo":
kwargs["queryset"] = Example.objects.select_related('bar')
return super().formfield_for_foreignkey(db_field, request, **kwargs)
For the sake of completeness, I would like to add another option that was the most suitable for my use case.
As others have pointed out, the problem is often loading the data for select boxes. list_select_related does not help in this case.
In case you don't actually want to edit the foreign key field via admin, the easiest fix is making the respective field readonly:
class Foo(admin.ModelAdmin):
readonly_fields = ('foreign_key_field1','foreign_key_field2',)
You can still display these fields, there will simply not be a select box, hence Django does not need to retrieve all the select box options from the database.

Optimizing ModelChoiceField query in django Admin (AppEngine)

I have two models: Activity and Place.
The Activity model has a ReferenceProperty to the Place model.
This was working fine until the Place table started growing and now
when trying to edit an Activity via django admin I get a memory error
from Google (it doesn't happen if I remove that field from the Activity
admin's fieldsets)
The widget used to edit the RefrenceProperty uses Place.all() to get
the possible values.
As both Activity and Place are sharded by a city property I would like
to optimize the widget's choice query from Place.all() to just the
relevant places, for example Place.all().filter("city =", )
I couldn't find a way to override the query in the docs and I was
wondering if the above is even possible? and if so, how?
Managed to optimize the query by overriding the admin form:
class ActivityAdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(ActivityAdminForm, self).__init__(*args, **kwargs)
self.fields['place'].queryset = <... my query ...>
class ActivityAdmin(admin.ModelAdmin):
form = ActivityAdminForm