mirror of
https://github.com/django/django.git
synced 2025-11-01 12:25:37 +00:00
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
8 changed files with 369 additions and 83 deletions
|
|
@ -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
|
||||
|
|
|
|||
112
tests/regressiontests/admin_widgets/tests.py
Normal file
112
tests/regressiontests/admin_widgets/tests.py
Normal file
|
|
@ -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)
|
||||
7
tests/regressiontests/admin_widgets/urls.py
Normal file
7
tests/regressiontests/admin_widgets/urls.py
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
from django.conf.urls.defaults import *
|
||||
import widgetadmin
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^', include(widgetadmin.site.urls)),
|
||||
)
|
||||
22
tests/regressiontests/admin_widgets/widgetadmin.py
Normal file
22
tests/regressiontests/admin_widgets/widgetadmin.py
Normal file
|
|
@ -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)
|
||||
Loading…
Add table
Add a link
Reference in a new issue