Cleaned up and refactored `ModelAdmin.formfield_for_dbfield`:
* The new method uses an admin configuration option (`formfield_overrides`); this makes custom admin widgets especially easy. * Refactored what was left of `formfield_for_dbfield` into a handful of smaller methods so that it's easier to hook in and return custom fields where needed. * These `formfield_for_*` methods now pass around `request` so that you can easily modify fields based on request (as in #3987). Fixes #8306, #3987, #9148. Thanks to James Bennet for the original patch; Alex Gaynor and Brian Rosner also contributed. git-svn-id: http://code.djangoproject.com/svn/django/trunk@9760 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
d579e716fe
commit
f212b24b64
|
@ -13,6 +13,7 @@ from django.shortcuts import get_object_or_404, render_to_response
|
|||
from django.utils.functional import update_wrapper
|
||||
from django.utils.html import escape
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.functional import curry
|
||||
from django.utils.text import capfirst, get_text_list
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.encoding import force_unicode
|
||||
|
@ -28,8 +29,28 @@ get_ul_class = lambda x: 'radiolist%s' % ((x == HORIZONTAL) and ' inline' or '')
|
|||
class IncorrectLookupParameters(Exception):
|
||||
pass
|
||||
|
||||
# Defaults for formfield_overrides. ModelAdmin subclasses can change this
|
||||
# by adding to ModelAdmin.formfield_overrides.
|
||||
|
||||
FORMFIELD_FOR_DBFIELD_DEFAULTS = {
|
||||
models.DateTimeField: {
|
||||
'form_class': forms.SplitDateTimeField,
|
||||
'widget': widgets.AdminSplitDateTime
|
||||
},
|
||||
models.DateField: {'widget': widgets.AdminDateWidget},
|
||||
models.TimeField: {'widget': widgets.AdminTimeWidget},
|
||||
models.TextField: {'widget': widgets.AdminTextareaWidget},
|
||||
models.URLField: {'widget': widgets.AdminURLFieldWidget},
|
||||
models.IntegerField: {'widget': widgets.AdminIntegerFieldWidget},
|
||||
models.CharField: {'widget': widgets.AdminTextInputWidget},
|
||||
models.ImageField: {'widget': widgets.AdminFileWidget},
|
||||
models.FileField: {'widget': widgets.AdminFileWidget},
|
||||
}
|
||||
|
||||
|
||||
class BaseModelAdmin(object):
|
||||
"""Functionality common to both ModelAdmin and InlineAdmin."""
|
||||
|
||||
raw_id_fields = ()
|
||||
fields = None
|
||||
exclude = None
|
||||
|
@ -39,6 +60,10 @@ class BaseModelAdmin(object):
|
|||
filter_horizontal = ()
|
||||
radio_fields = {}
|
||||
prepopulated_fields = {}
|
||||
formfield_overrides = {}
|
||||
|
||||
def __init__(self):
|
||||
self.formfield_overrides = dict(FORMFIELD_FOR_DBFIELD_DEFAULTS, **self.formfield_overrides)
|
||||
|
||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||
"""
|
||||
|
@ -47,101 +72,92 @@ class BaseModelAdmin(object):
|
|||
|
||||
If kwargs are given, they're passed to the form Field's constructor.
|
||||
"""
|
||||
request = kwargs.pop("request", None)
|
||||
|
||||
# If the field specifies choices, we don't need to look for special
|
||||
# admin widgets - we just need to use a select widget of some kind.
|
||||
if db_field.choices:
|
||||
if db_field.name in self.radio_fields:
|
||||
# If the field is named as a radio_field, use a RadioSelect
|
||||
return self.formfield_for_choice_field(db_field, request, **kwargs)
|
||||
|
||||
# ForeignKey or ManyToManyFields
|
||||
if isinstance(db_field, (models.ForeignKey, models.ManyToManyField)):
|
||||
# Combine the field kwargs with any options for formfield_overrides.
|
||||
# Make sure the passed in **kwargs override anything in
|
||||
# formfield_overrides because **kwargs is more specific, and should
|
||||
# always win.
|
||||
if db_field.__class__ in self.formfield_overrides:
|
||||
kwargs = dict(self.formfield_overrides[db_field.__class__], **kwargs)
|
||||
|
||||
# Get the correct formfield.
|
||||
if isinstance(db_field, models.ForeignKey):
|
||||
formfield = self.formfield_for_foreignkey(db_field, request, **kwargs)
|
||||
elif isinstance(db_field, models.ManyToManyField):
|
||||
formfield = self.formfield_for_manytomany(db_field, request, **kwargs)
|
||||
|
||||
# For non-raw_id fields, wrap the widget with a wrapper that adds
|
||||
# extra HTML -- the "add other" interface -- to the end of the
|
||||
# rendered output. formfield can be None if it came from a
|
||||
# OneToOneField with parent_link=True or a M2M intermediary.
|
||||
if formfield and db_field.name not in self.raw_id_fields:
|
||||
formfield.widget = widgets.RelatedFieldWidgetWrapper(formfield.widget, db_field.rel, self.admin_site)
|
||||
|
||||
return formfield
|
||||
|
||||
# If we've got overrides for the formfield defined, use 'em. **kwargs
|
||||
# passed to formfield_for_dbfield override the defaults.
|
||||
if db_field.__class__ in self.formfield_overrides:
|
||||
kwargs = dict(self.formfield_overrides[db_field.__class__], **kwargs)
|
||||
return db_field.formfield(**kwargs)
|
||||
|
||||
# For any other type of field, just call its formfield() method.
|
||||
return db_field.formfield(**kwargs)
|
||||
|
||||
def formfield_for_choice_field(self, db_field, request=None, **kwargs):
|
||||
"""
|
||||
Get a form Field for a database Field that has declared choices.
|
||||
"""
|
||||
# If the field is named as a radio_field, use a RadioSelect
|
||||
if db_field.name in self.radio_fields:
|
||||
# Avoid stomping on custom widget/choices arguments.
|
||||
if 'widget' not in kwargs:
|
||||
kwargs['widget'] = widgets.AdminRadioSelect(attrs={
|
||||
'class': get_ul_class(self.radio_fields[db_field.name]),
|
||||
})
|
||||
if 'choices' not in kwargs:
|
||||
kwargs['choices'] = db_field.get_choices(
|
||||
include_blank = db_field.blank,
|
||||
blank_choice=[('', _('None'))]
|
||||
)
|
||||
return db_field.formfield(**kwargs)
|
||||
else:
|
||||
# Otherwise, use the default select widget.
|
||||
return db_field.formfield(**kwargs)
|
||||
return db_field.formfield(**kwargs)
|
||||
|
||||
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
|
||||
"""
|
||||
Get a form Field for a ForeignKey.
|
||||
"""
|
||||
if db_field.name in self.raw_id_fields:
|
||||
kwargs['widget'] = widgets.ForeignKeyRawIdWidget(db_field.rel)
|
||||
elif db_field.name in self.radio_fields:
|
||||
kwargs['widget'] = widgets.AdminRadioSelect(attrs={
|
||||
'class': get_ul_class(self.radio_fields[db_field.name]),
|
||||
})
|
||||
kwargs['empty_label'] = db_field.blank and _('None') or None
|
||||
|
||||
# For DateTimeFields, use a special field and widget.
|
||||
if isinstance(db_field, models.DateTimeField):
|
||||
kwargs['form_class'] = forms.SplitDateTimeField
|
||||
kwargs['widget'] = widgets.AdminSplitDateTime()
|
||||
return db_field.formfield(**kwargs)
|
||||
return db_field.formfield(**kwargs)
|
||||
|
||||
def formfield_for_manytomany(self, db_field, request=None, **kwargs):
|
||||
"""
|
||||
Get a form Field for a ManyToManyField.
|
||||
"""
|
||||
# If it uses an intermediary model, don't show field in admin.
|
||||
if db_field.rel.through is not None:
|
||||
return None
|
||||
|
||||
# For DateFields, add a custom CSS class.
|
||||
if isinstance(db_field, models.DateField):
|
||||
kwargs['widget'] = widgets.AdminDateWidget
|
||||
return db_field.formfield(**kwargs)
|
||||
if db_field.name in self.raw_id_fields:
|
||||
kwargs['widget'] = widgets.ManyToManyRawIdWidget(db_field.rel)
|
||||
kwargs['help_text'] = ''
|
||||
elif db_field.name in (list(self.filter_vertical) + list(self.filter_horizontal)):
|
||||
kwargs['widget'] = widgets.FilteredSelectMultiple(db_field.verbose_name, (db_field.name in self.filter_vertical))
|
||||
|
||||
# For TimeFields, add a custom CSS class.
|
||||
if isinstance(db_field, models.TimeField):
|
||||
kwargs['widget'] = widgets.AdminTimeWidget
|
||||
return db_field.formfield(**kwargs)
|
||||
|
||||
# For TextFields, add a custom CSS class.
|
||||
if isinstance(db_field, models.TextField):
|
||||
kwargs['widget'] = widgets.AdminTextareaWidget
|
||||
return db_field.formfield(**kwargs)
|
||||
|
||||
# For URLFields, add a custom CSS class.
|
||||
if isinstance(db_field, models.URLField):
|
||||
kwargs['widget'] = widgets.AdminURLFieldWidget
|
||||
return db_field.formfield(**kwargs)
|
||||
|
||||
# For IntegerFields, add a custom CSS class.
|
||||
if isinstance(db_field, models.IntegerField):
|
||||
kwargs['widget'] = widgets.AdminIntegerFieldWidget
|
||||
return db_field.formfield(**kwargs)
|
||||
|
||||
# For CommaSeparatedIntegerFields, add a custom CSS class.
|
||||
if isinstance(db_field, models.CommaSeparatedIntegerField):
|
||||
kwargs['widget'] = widgets.AdminCommaSeparatedIntegerFieldWidget
|
||||
return db_field.formfield(**kwargs)
|
||||
|
||||
# For TextInputs, add a custom CSS class.
|
||||
if isinstance(db_field, models.CharField):
|
||||
kwargs['widget'] = widgets.AdminTextInputWidget
|
||||
return db_field.formfield(**kwargs)
|
||||
|
||||
# For FileFields and ImageFields add a link to the current file.
|
||||
if isinstance(db_field, models.ImageField) or isinstance(db_field, models.FileField):
|
||||
kwargs['widget'] = widgets.AdminFileWidget
|
||||
return db_field.formfield(**kwargs)
|
||||
|
||||
# For ForeignKey or ManyToManyFields, use a special widget.
|
||||
if isinstance(db_field, (models.ForeignKey, models.ManyToManyField)):
|
||||
if isinstance(db_field, models.ForeignKey) and db_field.name in self.raw_id_fields:
|
||||
kwargs['widget'] = widgets.ForeignKeyRawIdWidget(db_field.rel)
|
||||
elif isinstance(db_field, models.ForeignKey) and db_field.name in self.radio_fields:
|
||||
kwargs['widget'] = widgets.AdminRadioSelect(attrs={
|
||||
'class': get_ul_class(self.radio_fields[db_field.name]),
|
||||
})
|
||||
kwargs['empty_label'] = db_field.blank and _('None') or None
|
||||
else:
|
||||
if isinstance(db_field, models.ManyToManyField):
|
||||
# If it uses an intermediary model, don't show field in admin.
|
||||
if db_field.rel.through is not None:
|
||||
return None
|
||||
elif db_field.name in self.raw_id_fields:
|
||||
kwargs['widget'] = widgets.ManyToManyRawIdWidget(db_field.rel)
|
||||
kwargs['help_text'] = ''
|
||||
elif db_field.name in (list(self.filter_vertical) + list(self.filter_horizontal)):
|
||||
kwargs['widget'] = widgets.FilteredSelectMultiple(db_field.verbose_name, (db_field.name in self.filter_vertical))
|
||||
# Wrap the widget's render() method with a method that adds
|
||||
# extra HTML to the end of the rendered output.
|
||||
formfield = db_field.formfield(**kwargs)
|
||||
# Don't wrap raw_id fields. Their add function is in the popup window.
|
||||
if not db_field.name in self.raw_id_fields:
|
||||
# formfield can be None if it came from a OneToOneField with
|
||||
# parent_link=True
|
||||
if formfield is not None:
|
||||
formfield.widget = widgets.RelatedFieldWidgetWrapper(formfield.widget, db_field.rel, self.admin_site)
|
||||
return formfield
|
||||
|
||||
# For any other type of field, just call its formfield() method.
|
||||
return db_field.formfield(**kwargs)
|
||||
|
||||
def _declared_fieldsets(self):
|
||||
|
@ -292,7 +308,7 @@ class ModelAdmin(BaseModelAdmin):
|
|||
"form": self.form,
|
||||
"fields": fields,
|
||||
"exclude": exclude + kwargs.get("exclude", []),
|
||||
"formfield_callback": self.formfield_for_dbfield,
|
||||
"formfield_callback": curry(self.formfield_for_dbfield, request=request),
|
||||
}
|
||||
defaults.update(kwargs)
|
||||
return modelform_factory(self.model, **defaults)
|
||||
|
@ -837,7 +853,7 @@ class InlineModelAdmin(BaseModelAdmin):
|
|||
"fk_name": self.fk_name,
|
||||
"fields": fields,
|
||||
"exclude": exclude + kwargs.get("exclude", []),
|
||||
"formfield_callback": self.formfield_for_dbfield,
|
||||
"formfield_callback": curry(self.formfield_for_dbfield, request=request),
|
||||
"extra": self.extra,
|
||||
"max_num": self.max_num,
|
||||
}
|
||||
|
|
|
@ -597,6 +597,47 @@ with an operator:
|
|||
Performs a full-text match. This is like the default search method but uses
|
||||
an index. Currently this is only available for MySQL.
|
||||
|
||||
``formfield_overrides``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This provides a quick-and-dirty way to override some of the
|
||||
:class:`~django.forms.Field` options for use in the admin.
|
||||
``formfield_overrides`` is a dictionary mapping a field class to a dict of
|
||||
arguments to pass to the field at construction time.
|
||||
|
||||
Since that's a bit abstract, let's look at a concrete example. The most common
|
||||
use of ``formfield_overrides`` is to add a custom widget for a certain type of
|
||||
field. So, imagine we've written a ``RichTextEditorWidget`` that we'd like to
|
||||
use for large text fields instead of the default ``<textarea>``. Here's how we'd
|
||||
do that::
|
||||
|
||||
from django.db import models
|
||||
from django.contrib import admin
|
||||
|
||||
# Import our custom widget and our model from where they're defined
|
||||
from myapp.widgets import RichTextEditorWidget
|
||||
from myapp.models import MyModel
|
||||
|
||||
class MyModelAdmin(admin.ModelAdmin):
|
||||
formfield_overrides = {
|
||||
models.TextField: {'widget': RichTextEditorWidget},
|
||||
}
|
||||
|
||||
Note that the key in the dictionary is the actual field class, *not* a string.
|
||||
The value is another dictionary; these arguments will be passed to
|
||||
:meth:`~django.forms.Field.__init__`. See :ref:`ref-forms-api` for details.
|
||||
|
||||
.. warning::
|
||||
|
||||
If you want to use a custom widget with a relation field (i.e.
|
||||
:class:`~django.db.models.ForeignKey` or
|
||||
:class:`~django.db.models.ManyToManyField`), make sure you haven't included
|
||||
that field's name in ``raw_id_fields`` or ``radio_fields``.
|
||||
|
||||
``formfield_overrides`` won't let you change the widget on relation fields
|
||||
that have ``raw_id_fields`` or ``radio_fields`` set. That's because
|
||||
``raw_id_fields`` and ``radio_fields`` imply custom widgets of their own.
|
||||
|
||||
``ModelAdmin`` methods
|
||||
----------------------
|
||||
|
||||
|
@ -675,6 +716,23 @@ Notice the wrapped view in the fifth line above::
|
|||
|
||||
This wrapping will protect ``self.my_view`` from unauthorized access.
|
||||
|
||||
``formfield_for_foreignkey(self, db_field, request, **kwargs)``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The ``formfield_for_foreignkey`` method on a ``ModelAdmin`` allows you to
|
||||
override the default formfield for a foreign key field. For example, to
|
||||
return a subset of objects for this foreign key field based on the user::
|
||||
|
||||
class MyModelAdmin(admin.ModelAdmin):
|
||||
def formfield_for_foreignkey(self, db_field, request, **kwargs):
|
||||
if db_field.name == "car":
|
||||
kwargs["queryset"] = Car.object.filter(owner=request.user)
|
||||
return db_field.formfield(**kwargs)
|
||||
return super(MyModelAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
|
||||
|
||||
This uses the ``HttpRequest`` instance to filter the ``Car`` foreign key field
|
||||
to only the cars owned by the ``User`` instance.
|
||||
|
||||
``ModelAdmin`` media definitions
|
||||
--------------------------------
|
||||
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<django-objects version="1.0">
|
||||
<object pk="100" model="auth.user">
|
||||
<field type="CharField" name="username">super</field>
|
||||
<field type="CharField" name="first_name">Super</field>
|
||||
<field type="CharField" name="last_name">User</field>
|
||||
<field type="CharField" name="email">super@example.com</field>
|
||||
<field type="CharField" name="password">sha1$995a3$6011485ea3834267d719b4c801409b8b1ddd0158</field>
|
||||
<field type="BooleanField" name="is_staff">True</field>
|
||||
<field type="BooleanField" name="is_active">True</field>
|
||||
<field type="BooleanField" name="is_superuser">True</field>
|
||||
<field type="DateTimeField" name="last_login">2007-05-30 13:20:10</field>
|
||||
<field type="DateTimeField" name="date_joined">2007-05-30 13:20:10</field>
|
||||
<field to="auth.group" name="groups" rel="ManyToManyRel"></field>
|
||||
<field to="auth.permission" name="user_permissions" rel="ManyToManyRel"></field>
|
||||
</object>
|
||||
<object pk="101" model="auth.user">
|
||||
<field type="CharField" name="username">testser</field>
|
||||
<field type="CharField" name="first_name">Add</field>
|
||||
<field type="CharField" name="last_name">User</field>
|
||||
<field type="CharField" name="email">auser@example.com</field>
|
||||
<field type="CharField" name="password">sha1$995a3$6011485ea3834267d719b4c801409b8b1ddd0158</field>
|
||||
<field type="BooleanField" name="is_staff">True</field>
|
||||
<field type="BooleanField" name="is_active">True</field>
|
||||
<field type="BooleanField" name="is_superuser">False</field>
|
||||
<field type="DateTimeField" name="last_login">2007-05-30 13:20:10</field>
|
||||
<field type="DateTimeField" name="date_joined">2007-05-30 13:20:10</field>
|
||||
<field to="auth.group" name="groups" rel="ManyToManyRel"></field>
|
||||
<field to="auth.permission" name="user_permissions" rel="ManyToManyRel"></field>
|
||||
</object>
|
||||
|
||||
<object pk="1" model="admin_widgets.car">
|
||||
<field to="auth.user" name="owner" rel="ManyToOneRel">100</field>
|
||||
<field type="CharField" name="make">Volkswagon</field>
|
||||
<field type="CharField" name="model">Passat</field>
|
||||
</object>
|
||||
<object pk="2" model="admin_widgets.car">
|
||||
<field to="auth.user" name="owner" rel="ManyToOneRel">101</field>
|
||||
<field type="CharField" name="make">BMW</field>
|
||||
<field type="CharField" name="model">M3</field>
|
||||
</object>
|
||||
|
||||
</django-objects>
|
|
@ -2,9 +2,12 @@
|
|||
from django.conf import settings
|
||||
from django.db import models
|
||||
from django.core.files.storage import default_storage
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
class Member(models.Model):
|
||||
name = models.CharField(max_length=100)
|
||||
birthdate = models.DateTimeField(blank=True, null=True)
|
||||
gender = models.CharField(max_length=1, blank=True, choices=[('M','Male'), ('F', 'Female')])
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
@ -40,6 +43,28 @@ class Inventory(models.Model):
|
|||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
class Event(models.Model):
|
||||
band = models.ForeignKey(Band)
|
||||
date = models.DateField(blank=True, null=True)
|
||||
start_time = models.TimeField(blank=True, null=True)
|
||||
description = models.TextField(blank=True)
|
||||
link = models.URLField(blank=True)
|
||||
min_age = models.IntegerField(blank=True, null=True)
|
||||
|
||||
class Car(models.Model):
|
||||
owner = models.ForeignKey(User)
|
||||
make = models.CharField(max_length=30)
|
||||
model = models.CharField(max_length=30)
|
||||
|
||||
def __unicode__(self):
|
||||
return u"%s %s" % (self.make, self.model)
|
||||
|
||||
class CarTire(models.Model):
|
||||
"""
|
||||
A single car tire. This to test that a user can only select their own cars.
|
||||
"""
|
||||
car = models.ForeignKey(Car)
|
||||
|
||||
__test__ = {'WIDGETS_TESTS': """
|
||||
>>> from datetime import datetime
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
from django import forms
|
||||
from django.contrib import admin
|
||||
from django.contrib.admin import widgets
|
||||
from unittest import TestCase
|
||||
from django.test import TestCase as DjangoTestCase
|
||||
import models
|
||||
|
||||
class AdminFormfieldForDBFieldTests(TestCase):
|
||||
"""
|
||||
Tests for correct behavior of ModelAdmin.formfield_for_dbfield
|
||||
"""
|
||||
|
||||
def assertFormfield(self, model, fieldname, widgetclass, **admin_overrides):
|
||||
"""
|
||||
Helper to call formfield_for_dbfield for a given model and field name
|
||||
and verify that the returned formfield is appropriate.
|
||||
"""
|
||||
# Override any settings on the model admin
|
||||
class MyModelAdmin(admin.ModelAdmin): pass
|
||||
for k in admin_overrides:
|
||||
setattr(MyModelAdmin, k, admin_overrides[k])
|
||||
|
||||
# Construct the admin, and ask it for a formfield
|
||||
ma = MyModelAdmin(model, admin.site)
|
||||
ff = ma.formfield_for_dbfield(model._meta.get_field(fieldname), request=None)
|
||||
|
||||
# "unwrap" the widget wrapper, if needed
|
||||
if isinstance(ff.widget, widgets.RelatedFieldWidgetWrapper):
|
||||
widget = ff.widget.widget
|
||||
else:
|
||||
widget = ff.widget
|
||||
|
||||
# Check that we got a field of the right type
|
||||
self.assert_(
|
||||
isinstance(widget, widgetclass),
|
||||
"Wrong widget for %s.%s: expected %s, got %s" % \
|
||||
(model.__class__.__name__, fieldname, widgetclass, type(widget))
|
||||
)
|
||||
|
||||
# Return the formfield so that other tests can continue
|
||||
return ff
|
||||
|
||||
def testDateField(self):
|
||||
self.assertFormfield(models.Event, 'date', widgets.AdminDateWidget)
|
||||
|
||||
def testDateTimeField(self):
|
||||
self.assertFormfield(models.Member, 'birthdate', widgets.AdminSplitDateTime)
|
||||
|
||||
def testTimeField(self):
|
||||
self.assertFormfield(models.Event, 'start_time', widgets.AdminTimeWidget)
|
||||
|
||||
def testTextField(self):
|
||||
self.assertFormfield(models.Event, 'description', widgets.AdminTextareaWidget)
|
||||
|
||||
def testURLField(self):
|
||||
self.assertFormfield(models.Event, 'link', widgets.AdminURLFieldWidget)
|
||||
|
||||
def testIntegerField(self):
|
||||
self.assertFormfield(models.Event, 'min_age', widgets.AdminIntegerFieldWidget)
|
||||
|
||||
def testCharField(self):
|
||||
self.assertFormfield(models.Member, 'name', widgets.AdminTextInputWidget)
|
||||
|
||||
def testFileField(self):
|
||||
self.assertFormfield(models.Album, 'cover_art', widgets.AdminFileWidget)
|
||||
|
||||
def testForeignKey(self):
|
||||
self.assertFormfield(models.Event, 'band', forms.Select)
|
||||
|
||||
def testRawIDForeignKey(self):
|
||||
self.assertFormfield(models.Event, 'band', widgets.ForeignKeyRawIdWidget,
|
||||
raw_id_fields=['band'])
|
||||
|
||||
def testRadioFieldsForeignKey(self):
|
||||
ff = self.assertFormfield(models.Event, 'band', widgets.AdminRadioSelect,
|
||||
radio_fields={'band':admin.VERTICAL})
|
||||
self.assertEqual(ff.empty_label, None)
|
||||
|
||||
def testManyToMany(self):
|
||||
self.assertFormfield(models.Band, 'members', forms.SelectMultiple)
|
||||
|
||||
def testRawIDManyTOMany(self):
|
||||
self.assertFormfield(models.Band, 'members', widgets.ManyToManyRawIdWidget,
|
||||
raw_id_fields=['members'])
|
||||
|
||||
def testFilteredManyToMany(self):
|
||||
self.assertFormfield(models.Band, 'members', widgets.FilteredSelectMultiple,
|
||||
filter_vertical=['members'])
|
||||
|
||||
def testFormfieldOverrides(self):
|
||||
self.assertFormfield(models.Event, 'date', forms.TextInput,
|
||||
formfield_overrides={'widget': forms.TextInput})
|
||||
|
||||
def testFieldWithChoices(self):
|
||||
self.assertFormfield(models.Member, 'gender', forms.Select)
|
||||
|
||||
def testChoicesWithRadioFields(self):
|
||||
self.assertFormfield(models.Member, 'gender', widgets.AdminRadioSelect,
|
||||
radio_fields={'gender':admin.VERTICAL})
|
||||
|
||||
|
||||
class AdminFormfieldForDBFieldWithRequestTests(DjangoTestCase):
|
||||
fixtures = ["admin-widgets-users.xml"]
|
||||
|
||||
def testFilterChoicesByRequestUser(self):
|
||||
"""
|
||||
Ensure the user can only see their own cars in the foreign key dropdown.
|
||||
"""
|
||||
self.client.login(username="super", password="secret")
|
||||
response = self.client.get("/widget_admin/admin_widgets/cartire/add/")
|
||||
self.assert_("BMW M3" not in response.content)
|
||||
self.assert_("Volkswagon Passat" in response.content)
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
from django.conf.urls.defaults import *
|
||||
import widgetadmin
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^', include(widgetadmin.site.urls)),
|
||||
)
|
|
@ -0,0 +1,22 @@
|
|||
"""
|
||||
|
||||
"""
|
||||
from django.contrib import admin
|
||||
|
||||
import models
|
||||
|
||||
class WidgetAdmin(admin.AdminSite):
|
||||
pass
|
||||
|
||||
|
||||
class CarTireAdmin(admin.ModelAdmin):
|
||||
def formfield_for_foreignkey(self, db_field, request, **kwargs):
|
||||
if db_field.name == "car":
|
||||
kwargs["queryset"] = models.Car.objects.filter(owner=request.user)
|
||||
return db_field.formfield(**kwargs)
|
||||
return super(CarTireAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
|
||||
|
||||
site = WidgetAdmin()
|
||||
|
||||
site.register(models.Car)
|
||||
site.register(models.CarTire, CarTireAdmin)
|
|
@ -24,6 +24,9 @@ urlpatterns = patterns('',
|
|||
# admin view tests
|
||||
(r'^test_admin/', include('regressiontests.admin_views.urls')),
|
||||
(r'^generic_inline_admin/', include('regressiontests.generic_inline_admin.urls')),
|
||||
|
||||
# admin widget tests
|
||||
(r'widget_admin/', include('regressiontests.admin_widgets.urls')),
|
||||
|
||||
(r'^utils/', include('regressiontests.utils.urls')),
|
||||
|
||||
|
|
Loading…
Reference in New Issue