在Django Admin中调整字段大小

问题:在Django Admin中调整字段大小

Django倾向于在管理员上添加或编辑条目时填充水平空间,但是在某些情况下,这是真正的空间浪费,例如,编辑日期字段(8个字符宽)或CharField(也可以是6或8)字符宽,然后编辑框最多为15或20个字符。

如何告诉管理员文本框的宽度或TextField编辑框的高度?

Django tends to fill up horizontal space when adding or editing entries on the admin, but, in some cases, is a real waste of space, when, i.e., editing a date field, 8 characters wide, or a CharField, also 6 or 8 chars wide, and then the edit box goes up to 15 or 20 chars.

How can I tell the admin how wide a textbox should be, or the height of a TextField edit box?


回答 0

您应该使用ModelAdmin.formfield_overrides

这很容易- admin.py定义:

from django.forms import TextInput, Textarea
from django.db import models

class YourModelAdmin(admin.ModelAdmin):
    formfield_overrides = {
        models.CharField: {'widget': TextInput(attrs={'size':'20'})},
        models.TextField: {'widget': Textarea(attrs={'rows':4, 'cols':40})},
    }

admin.site.register(YourModel, YourModelAdmin)

You should use ModelAdmin.formfield_overrides.

It is quite easy – in admin.py, define:

from django.forms import TextInput, Textarea
from django.db import models

class YourModelAdmin(admin.ModelAdmin):
    formfield_overrides = {
        models.CharField: {'widget': TextInput(attrs={'size':'20'})},
        models.TextField: {'widget': Textarea(attrs={'rows':4, 'cols':40})},
    }

admin.site.register(YourModel, YourModelAdmin)

回答 1

您可以使用其“ attrs”属性在窗口小部件上设置任意HTML属性。

您可以在Django管理员中使用formfield_for_dbfield进行此操作:

class MyModelAdmin(admin.ModelAdmin):
  def formfield_for_dbfield(self, db_field, **kwargs):
    field = super(ContentAdmin, self).formfield_for_dbfield(db_field, **kwargs)
    if db_field.name == 'somefield':
      field.widget.attrs['class'] = 'someclass ' + field.widget.attrs.get('class', '')
    return field

或带有自定义Widget子类和formfield_overrides字典

class DifferentlySizedTextarea(forms.Textarea):
  def __init__(self, *args, **kwargs):
    attrs = kwargs.setdefault('attrs', {})
    attrs.setdefault('cols', 80)
    attrs.setdefault('rows', 5)
    super(DifferentlySizedTextarea, self).__init__(*args, **kwargs)

class MyModelAdmin(admin.ModelAdmin):
  formfield_overrides = { models.TextField: {'widget': DifferentlySizedTextarea}}

You can set arbitrary HTML attributes on a widget using its “attrs” property.

You can do this in the Django admin using formfield_for_dbfield:

class MyModelAdmin(admin.ModelAdmin):
  def formfield_for_dbfield(self, db_field, **kwargs):
    field = super(ContentAdmin, self).formfield_for_dbfield(db_field, **kwargs)
    if db_field.name == 'somefield':
      field.widget.attrs['class'] = 'someclass ' + field.widget.attrs.get('class', '')
    return field

or with a custom Widget subclass and the formfield_overrides dictionary:

class DifferentlySizedTextarea(forms.Textarea):
  def __init__(self, *args, **kwargs):
    attrs = kwargs.setdefault('attrs', {})
    attrs.setdefault('cols', 80)
    attrs.setdefault('rows', 5)
    super(DifferentlySizedTextarea, self).__init__(*args, **kwargs)

class MyModelAdmin(admin.ModelAdmin):
  formfield_overrides = { models.TextField: {'widget': DifferentlySizedTextarea}}

回答 2

更改特定字段的宽度。

通过ModelAdmin.get_form制成

class YourModelAdmin(admin.ModelAdmin):
    def get_form(self, request, obj=None, **kwargs):
        form = super(YourModelAdmin, self).get_form(request, obj, **kwargs)
        form.base_fields['myfield'].widget.attrs['style'] = 'width: 45em;'
        return form

To change the width for a specific field.

Made via ModelAdmin.get_form:

class YourModelAdmin(admin.ModelAdmin):
    def get_form(self, request, obj=None, **kwargs):
        form = super(YourModelAdmin, self).get_form(request, obj, **kwargs)
        form.base_fields['myfield'].widget.attrs['style'] = 'width: 45em;'
        return form

回答 3

一种快速而肮脏的选择是简单地为所讨论的模型提供自定义模板。

如果您创建一个名为的模板,admin/<app label>/<class name>/change_form.html则管理员将使用该模板代替默认模板。也就是说,如果您在名为Person的应用中有一个名为的模型people,则可以创建一个名为的模板admin/people/person/change_form.html

所有管理模板都有一个extrahead块,您可以覆盖该块以将内容放入中<head>,而难题的最后一部分是每个字段的HTML ID为id_<field-name>

因此,您可以在模板中添加以下内容:

{% extends "admin/change_form.html" %}

{% block extrahead %}
  {{ block.super }}
  <style type="text/css">
    #id_my_field { width: 100px; }
  </style>
{% endblock %}

A quick and dirty option is to simply provide a custom template for the model in question.

If you create a template named admin/<app label>/<class name>/change_form.html then the admin will use that template instead of the default. That is, if you’ve got a model named Person in an app named people, you’d create a template named admin/people/person/change_form.html.

All the admin templates have an extrahead block you can override to place stuff in the <head>, and the final piece of the puzzle is the fact that every field has an HTML id of id_<field-name>.

So, you could put something like the following in your template:

{% extends "admin/change_form.html" %}

{% block extrahead %}
  {{ block.super }}
  <style type="text/css">
    #id_my_field { width: 100px; }
  </style>
{% endblock %}

回答 4

如果要更改每个字段实例的属性,可以将“ attrs”属性直接添加到表单条目中。

例如:

class BlogPostForm(forms.ModelForm):
    title = forms.CharField(label='Title:', max_length=128)
    body = forms.CharField(label='Post:', max_length=2000, 
        widget=forms.Textarea(attrs={'rows':'5', 'cols': '5'}))

    class Meta:
        model = BlogPost
        fields = ('title', 'body')

“ attrs”属性基本上沿HTML标记传递,它将调整表单字段。每个条目都是您要覆盖的属性的元组,以及您要覆盖其的值。您可以输入任意多个属性,只要用逗号分隔每个元组即可。

If you want to change the attributes on a per-field instance, you can add the “attrs” property directly in to your form entries.

for example:

class BlogPostForm(forms.ModelForm):
    title = forms.CharField(label='Title:', max_length=128)
    body = forms.CharField(label='Post:', max_length=2000, 
        widget=forms.Textarea(attrs={'rows':'5', 'cols': '5'}))

    class Meta:
        model = BlogPost
        fields = ('title', 'body')

The “attrs” property basically passes along the HTML markup that will adjust the form field. Each entry is a tuple of the attribute you would like to override and the value you would like to override it with. You can enter as many attributes as you like as long as you separate each tuple with a comma.


回答 5

我发现最好的方法是这样的:

class NotificationForm(forms.ModelForm):
    def __init__(self, *args, **kwargs): 
        super(NotificationForm, self).__init__(*args, **kwargs)
        self.fields['content'].widget.attrs['cols'] = 80
        self.fields['content'].widget.attrs['rows'] = 15
        self.fields['title'].widget.attrs['size'] = 50
    class Meta:
        model = Notification

对于ModelForm而言,它比使用不同的小部件覆盖字段要好得多,因为它可以保留namehelp_text属性以及模型字段的默认值,因此您不必将它们复制到表单中。

The best way I found is something like this:

class NotificationForm(forms.ModelForm):
    def __init__(self, *args, **kwargs): 
        super(NotificationForm, self).__init__(*args, **kwargs)
        self.fields['content'].widget.attrs['cols'] = 80
        self.fields['content'].widget.attrs['rows'] = 15
        self.fields['title'].widget.attrs['size'] = 50
    class Meta:
        model = Notification

Its much better for ModelForm than overriding fields with different widgets, as it preserves name and help_text attributes and also default values of model fields, so you don’t have to copy them to your form.


回答 6

我在TextField中遇到了类似的问题。我正在使用Django 1.0.2,并且想要在关联的textarea中更改“行”的默认值。该版本不存在formfield_overrides。重写formfield_for_dbfield可以,但是我必须对每个ModelAdmin子类都这样做,否则会导致递归错误。最终,我发现将以下代码添加到models.py可以正常工作:

from django.forms import Textarea

class MyTextField(models.TextField):
#A more reasonably sized textarea                                                                                                            
    def formfield(self, **kwargs):
         kwargs.update(
            {"widget": Textarea(attrs={'rows':2, 'cols':80})}
         )
         return super(MyTextField, self).formfield(**kwargs)

然后在定义模型时使用MyTextField而不是TextField。我从这个答案中将其改编为类似的问题。

I had a similar problem with TextField. I’m using Django 1.0.2 and wanted to change the default value for ‘rows’ in the associated textarea. formfield_overrides doesn’t exist in this version. Overriding formfield_for_dbfield worked but I had to do it for each of my ModelAdmin subclasses or it would result in a recursion error. Eventually, I found that adding the code below to models.py works:

from django.forms import Textarea

class MyTextField(models.TextField):
#A more reasonably sized textarea                                                                                                            
    def formfield(self, **kwargs):
         kwargs.update(
            {"widget": Textarea(attrs={'rows':2, 'cols':80})}
         )
         return super(MyTextField, self).formfield(**kwargs)

Then use MyTextField instead of TextField when defining your models. I adapted it from this answer to a similar question.


回答 7

Django FAQ对此进行了很好的描述:

问:如何更改模型中字段上小部件的属性?

答:覆盖ModelAdmin / StackedInline / TabularInline类中的formfield_for_dbfield

class MyOtherModelInline(admin.StackedInline):
    model = MyOtherModel
    extra = 1

    def formfield_for_dbfield(self, db_field, **kwargs):
        # This method will turn all TextFields into giant TextFields
        if isinstance(db_field, models.TextField):
            return forms.CharField(widget=forms.Textarea(attrs={'cols': 130, 'rows':30, 'class': 'docx'}))
        return super(MyOtherModelInline, self).formfield_for_dbfield(db_field, **kwargs)

It’s well described in Django FAQ:

Q: How do I change the attributes for a widget on a field in my model?

A: Override the formfield_for_dbfield in the ModelAdmin/StackedInline/TabularInline class

class MyOtherModelInline(admin.StackedInline):
    model = MyOtherModel
    extra = 1

    def formfield_for_dbfield(self, db_field, **kwargs):
        # This method will turn all TextFields into giant TextFields
        if isinstance(db_field, models.TextField):
            return forms.CharField(widget=forms.Textarea(attrs={'cols': 130, 'rows':30, 'class': 'docx'}))
        return super(MyOtherModelInline, self).formfield_for_dbfield(db_field, **kwargs)

回答 8

您总是可以在自定义样式表中设置字段大小,并告诉Django将其用于ModelAdmin类:

class MyModelAdmin(ModelAdmin):
    class Media:
        css = {"all": ("my_stylesheet.css",)}

You can always set your fields sizes in a custom stylesheet and tell Django to use that for your ModelAdmin class:

class MyModelAdmin(ModelAdmin):
    class Media:
        css = {"all": ("my_stylesheet.css",)}

回答 9

对于1.6,使用表格我必须在charfield内指定textarea的属性:

test1 = forms.CharField(max_length=400, widget=forms.Textarea( attrs={'rows':'2', 'cols': '10'}),  initial='', help_text=helptexts.helptxt['test'])

for 1.6, using forms I had to specify the attributes of the textarea inside the charfield:

test1 = forms.CharField(max_length=400, widget=forms.Textarea( attrs={'rows':'2', 'cols': '10'}),  initial='', help_text=helptexts.helptxt['test'])

回答 10

与msdin的答案相同,但使用TextInput而不是TextArea:

from django.forms import TextInput

class ShortTextField(models.TextField):
    def formfield(self, **kwargs):
         kwargs.update(
            {"widget": TextInput(attrs={'size': 10})}
         )
         return super(ShortTextField, self).formfield(**kwargs)

Same answer as msdin but with TextInput instead of TextArea:

from django.forms import TextInput

class ShortTextField(models.TextField):
    def formfield(self, **kwargs):
         kwargs.update(
            {"widget": TextInput(attrs={'size': 10})}
         )
         return super(ShortTextField, self).formfield(**kwargs)

回答 11

这是一个简单但灵活的解决方案。使用自定义表单覆盖某些小部件。

# models.py
class Elephant(models.Model):
    name = models.CharField(max_length=25)
    age = models.IntegerField()

# forms.py
class ElephantForm(forms.ModelForm):

    class Meta:
        widgets = {
            'age': forms.TextInput(attrs={'size': 3}),
        }

# admin.py
@admin.register(Elephant)
class ElephantAdmin(admin.ModelAdmin):
    form = ElephantForm

中提供的小部件 ElephantForm将替换默认的。关键是字段的字符串表示形式。表单中未指定的字段将使用默认窗口小部件。

请注意,尽管age是,但IntegerField我们可以使用该TextInput小部件,因为与不同NumberInput,它TextInput接受size属性。

本文介绍此解决方案。

Here is a simple, yet flexible solution. Use a custom form to override some widgets.

# models.py
class Elephant(models.Model):
    name = models.CharField(max_length=25)
    age = models.IntegerField()

# forms.py
class ElephantForm(forms.ModelForm):

    class Meta:
        widgets = {
            'age': forms.TextInput(attrs={'size': 3}),
        }

# admin.py
@admin.register(Elephant)
class ElephantAdmin(admin.ModelAdmin):
    form = ElephantForm

The widgets given in ElephantForm will replace the default ones. The key is the string representation of the field. Fields not specified in the form will use the default widget.

Note that although age is an IntegerField we can use the TextInput widget, because unlike the NumberInput, TextInput accepts the size attribute.

This solution is described in this article.


回答 12

如果您正在使用涉及选项/选项/下拉菜单的ForeignKey字段,则可以formfield_for_foreignkey在Admin实例中覆盖:

class YourNewAdmin(admin.ModelAdmin):
    ...

    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == 'your_fk_field':
            """ For your FK field of choice, override the dropdown style """
            kwargs["widget"] = django.forms.widgets.Select(attrs={
                'style': 'width: 250px;'
            })

        return super().formfield_for_foreignkey(db_field, request, **kwargs)

这种模式的更多信息,在这里这里

If you are working with a ForeignKey field that involves choices/options/a dropdown menu, you can override formfield_for_foreignkey in the Admin instance:

class YourNewAdmin(admin.ModelAdmin):
    ...

    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == 'your_fk_field':
            """ For your FK field of choice, override the dropdown style """
            kwargs["widget"] = django.forms.widgets.Select(attrs={
                'style': 'width: 250px;'
            })

        return super().formfield_for_foreignkey(db_field, request, **kwargs)

More information on this pattern here and here.


回答 13

还有另一个例子:

class SecenekInline(admin.TabularInline):
   model = Secenek
   # classes = ['collapse']
   def formfield_for_dbfield(self, db_field, **kwargs):
       field = super(SecenekInline, self).formfield_for_dbfield(db_field, **kwargs)
       if db_field.name == 'harf':
           field.widget = TextInput(attrs={'size':2})
       return field
   formfield_overrides = {
       models.TextField: {'widget': Textarea(attrs={'rows':2})},
   }
   extra = 2

如果只想编辑特定的字段大小,则可以使用它。

And one more example too :

class SecenekInline(admin.TabularInline):
   model = Secenek
   # classes = ['collapse']
   def formfield_for_dbfield(self, db_field, **kwargs):
       field = super(SecenekInline, self).formfield_for_dbfield(db_field, **kwargs)
       if db_field.name == 'harf':
           field.widget = TextInput(attrs={'size':2})
       return field
   formfield_overrides = {
       models.TextField: {'widget': Textarea(attrs={'rows':2})},
   }
   extra = 2

If you want to edit only a specific fields size, you can use this.