Django Guardian has two forms defined in admin.py, GroupManage and UserManage: https://github.com/lukaszb/django-guardian/blob/master/guardian/admin.py#L368
I would like to add auto-completion to these two forms, and the best way I assume to make that happen is to overwrite the group and user's field widgets (my first attempt uses django autocomplete_light.) The goal is to not need to fork django guardian.
So in my app's models.py, I added the following code
GroupManage.__class__.group = forms.CharField(max_length=81,
error_messages={'does_not_exist':
"This group does not exist!"}, widget=ChoiceWidget(True))
I also tried using setattr to no avail. In the django shell it acts like this should be working, but when the admin page gets loaded the old group variable is restored, with the default CharField widget.
The fields defined for the class are stored in the dictionary base_fields.
GroupManage.base_fields['group'] = forms.CharField(max_length=81,
error_messages={'does_not_exist':
"This group does not exist!"}, widget=ChoiceWidget(True))
Sometimes, it might be easier to alter a field attribute instead of replacing the entire field:
GroupManage.base_fields['group'].help_text = "New help text"
Related
I am defining a field in a django form in the following two ways :
class MyForm(forms.Form):
myfield = forms.ChoiceField(choices=[(u.id,u.username) for u in User.objects.filter(type="TYPE1")])
OR
class MyForm(forms.Form):
pass
def_init_(self,*args,**kwargs):
super(MyForm,self)._init_(*args,**kwargs)
self.fields['myfield'] = forms.ChoiceField(choices=[(u.id,u.username) for u in User.objects.filter(type="TYPE1")])
Is there any different between these two approaches?? I tried to find this on web but did not get any relevant answers.
First of all, there is a special field to handle such things - ModelChoiceField
In your example the difference is the moment when code is executed.
In first approach it is executed when module with form is loaded, in second - every time the form is initialized (so basically on each request to your view). So first approach has a problem - Users will be loaded on first request. If any user registers after this moment - he will not be present in the select field until you restart the server.
Also I think it is a bad practice to introduce new field in __init__ method. If you really need something like this and you can't use ModelChoiceField the better way is
class MyForm(forms.Form):
myfield = forms.ChoiceField()
def __init__(self,*args,**kwargs):
super(MyForm,self).__init__(*args,**kwargs)
self.fields['myfield'].choices = [(u.id, u.username) for u in User.objects.filter(type="TYPE1")]
Don't you get an error when you run your server using the first way?
class MyForm(forms.Form):
myfield = forms.ChoiceField(choices=[(u.id,u.username) for u in User.objects.filter(type="TYPE1")])
If you use this method That for will be executed when the file is being read. Try using the second way or a function to execute that for and set the choices.
Usually in Form.__init__ you define some dynamic fields (for example, you want to show some checkboxes, which are set/unset according to the data from some model). In all other cases first approach is better because it's more readable.
I think I have a pretty common use case and am surprised at how much trouble it's giving me.
I want to use a key-value pair for a ReferenceField in the Flask-Admin edit form generated by the following two classes:
class Communique(db.Document):
users = db.ListField(db.ReferenceField(User), default=[])
class User(db.Document):
email = db.StringField(max_length=255, required=True)
def __unicode__(self):
return '%s' % self.id
I want the select to be constructed out of the ObjectId and the an email field in my model.
By mapping the __unicode__
attribute to the id field I get nice things on the mongoengine side like using the entire object in queries:
UserInformation.objects(user=current_user)
This has the unfortunate effect of causing the Flask-Admin form to display the mongo ObjectId in the edit form like so:
The docs say I have to provide the label_attr to the ModelSelectMultipleField created by Flask-Admin. I've done so by overriding the get_form method on my ModelView:
def get_form(self):
form = super(ModelView, self).get_form()
form.users = ModelSelectMultipleField(model=User,
label_attr='email',
widget=form.users.__dict__['kwargs']['widget'])
return form
I'm reusing the the widget used by the original form.users (which may be wrong). It works fine when editing an existing item, BUT throws an exception when creating a new one (perhaps because I'm reusing the widget).
All of this seems like way more work than should be needed to simply provide a label_attr to my SelectField. Fixing up the listing view was a simple matter of adding an entry to the column_formatters dictionary. Is there no simple way to specify the label_attr when creating my ModelView class?
I know I could make this problem go away by returning the email property in the __unicode__ attribute, but I feel like I shouldn't have to do that! Am I missing something?
Oy, now I see how to do it, though it's not that obvious from the docs. form_args is a dictionary with items keyed to the form models. All I needed to do was...
form_args = dict(users=dict(label_attr='email'))
Which does seem about the right amount of effort (considering Flask-Admin isn't some sort of java framework).
I want to add a specified icon for each model on admin index page. I added an attribute named "picture" on each model then I modified /contrib/admin/sites.py to pass that picture name to template and checked and use it on index.html template of admin to get the result.
I wonder to know if there is a better way
class Product(models.Model):
abbr = models.CharField(max_length=20,unique=True)
title = models.CharField(max_length=200,unique=True)
owner = models.ForeignKey(UserProxy)
des = models.TextField(blank=True,null=True)
picture = 'product.png'
def __unicode__(self):
return self.abbr
class Meta:
none
What You did seems OK, only small tips that can make Your code a little better:
Instead of modifying django/contrib/admin/sites.py You can subclass the AdminSite class (if You didn't do that already).
Modify the AdminSite.index() method to pass not picture, but whole admin class (there is a model_admin variable available in the index() method).
Assign picture in the ModelAdmin classes, not models, to separate admin stuff from models.
The answer from 'Python Fanboy' was so brief but useful to me, I could avoid modifying django base classes
picture field were moved to admin class
I subclass AdminSite as CustomAdminSite and copied index and app_index and made modification
(I don't know if there is a better way than copying whole of index and app_index like overriding)
In urls.py, 'admin.sites.url' replaced by 'custom_site.url'
(custom_site is instance of CustomAdminSite)
I did not want another url instead of '/admin' like '/my_admin' so I have to use instance of CustomAdminSite for all my models even User, Group & Site
I'm using admin_tools, now I've lose my application menu
Any better Idea or solution for my new encountered problems?
Here is an abstract base class for many of my "Treatment" models (TreatmentA, TreatmentB, etc):
class TreatmentBase(models.Model):
URL_PREFIX = '' # child classes define this string
code = shared.models.common.RandomCharField(length=6)
class Meta:
abstract = True
Each Treatment instance has a URL, that when visited by a user, takes them to a page specific to that treatment. I want to be able to create a Treatment in Django Admin, and immediately get this URL so I can send it to users. This URL can be created with the following method on TreatmentBase:
def get_url(self):
return '{}/{}/'.format(self.URL_PREFIX, self.code)
However, I am stuck with how to get this URL to display in Django Admin. I can think of the following solutions:
(1) Customize the display of the code field so that it becomes a clickable URL. Problem: I don't know how to do this.
(2) Add the get_url method to ModelAdmin.list_display. Problem: This means I would have to define a separate list_display for each of the child models of BaseTreatment, and I would have to explicitly list all the fields of the model, meaning I have to update it every time I modify a model, violating DRY.
(3) Add an extra field like this:
url = models.URLField(default = get_url)
Problem: get_url is an instance method (since it needs to refer to the self.code field), and from my reading of the docs about the default argument, it just has to be a simple callable without arguments.
Any way to do this seemingly simple task?
You could go with option 2 (adding to the admin display) but add it to the
readonly_fields which may alleviate your DRY concerns when models changes.
Option 3 (the extra field) could also work if you override the save method setting the URL property. You'd either want to set the field as readonly in the admin or only set the value in the save method if it's currently None.
I have a legacy database with non-django naming conventions. If I have the following (cut down) models:
class Registration(models.Model):
projectId=models.IntegerField(primary_key=True)
class Application(models.Model):
applicationId=models.IntegerField(primary_key=True)
registration=models.ForeignKey(Registration,db_column='projectId')
The ForeignKey instance causes a property to be created on Application called registration_id, but this is neither the correct name for the field (I have a hack to fix this), nor is it able to be used in a QuerySet.
Is there some way of using the id field provided by the ForeignKey on the Application model, rather than having to reference it via Registration?
Ie. I write lots of code like:
Application.objects.get(projectId=1234)
And don't want to have to write it out as:
Application.objects.get(registration__projectId=1234)
or even
Application.objects.get(registration__pk=1234)
I'm slightly surprised that:
Application.objects.get(registration_id=1234)
doesn't work...
Also note, I tried defining the id column as a field as well as the foreignkey which worked for queryset, but inserts complain of trying to insert into the same column twice:
class Application(models.Model):
...
projectId=models.IntegerField()
...
Have you tried this?
Application.objects.get(registration=1234)
I think just doing Application.objects.registration.get(projectId=1234) should do what you want.