ปรับขนาดฟิลด์ใน Django Admin


109

Django มีแนวโน้มที่จะเติมช่องว่างแนวนอนเมื่อเพิ่มหรือแก้ไขรายการในผู้ดูแลระบบ แต่ในบางกรณีจะเป็นการสิ้นเปลืองพื้นที่อย่างแท้จริงเมื่อแก้ไขฟิลด์วันที่กว้าง 8 อักขระหรือ CharField เช่นกัน 6 หรือ 8 กว้างตัวอักษรจากนั้นช่องแก้ไขจะเพิ่มขึ้นเป็น 15 หรือ 20 ตัวอักษร

ฉันจะบอกผู้ดูแลระบบได้อย่างไรว่ากล่องข้อความควรกว้างแค่ไหนหรือความสูงของกล่องแก้ไข TextField


9
เห็นได้ชัดว่าคุณลืมทำเช่นนั้นในกรณีนี้ มากกว่าสองปีต่อมา =)
casperOne

โครงการถูกยกเลิกและฉันไม่ได้เห็นรหัสมาระยะหนึ่งแล้ว ฉันอาจจะเริ่มโครงการใหม่ในเดือนหน้าดังนั้นฉันอาจจะทำสิ่งนี้อีกครั้ง: D
Andor

คำตอบ:


211

คุณควรใช้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)

1
นี่คือสิ่งที่ฉันกำลังมองหาโดยไม่ต้องปรับแต่งเทมเพลตที่ไร้สาระ ขอบคุณ!
คริส

2
โปรดทราบว่าสิ่งนี้จะไม่ทำงานหากfilter_horizontalหรือfilter_verticalถูกตั้งค่าสำหรับฟิลด์ที่เกี่ยวข้องใน YourModelAdmin ฉันใช้เวลาพอสมควรในการคิดออก
Dennis Golomazov

ในรูปแบบนี้มีอะไรที่น่ากลัวหรือไม่? ฉันไม่พบวิธีตั้งค่าแอตทริบิวต์ attrs สำหรับ Textareas ทั้งหมด
Julian

1
ฉันใช้อย่างเดียวmodels.TextField: {'widget': Textarea(attrs={'rows':4})}แต่ความกว้างก็เล็กลงด้วย
Smit Johnth

ยากที่จะมีขนาดเท่ากัน
Sören

45

คุณสามารถตั้งค่าโดยพล HTML แอตทริบิวต์ที่วิดเจ็ตโดยใช้ของทรัพย์สิน "attrs"

คุณสามารถทำได้ในผู้ดูแลระบบ 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

หรือด้วยคลาสย่อยวิดเจ็ตที่กำหนดเองและพจนานุกรม 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}}

30

เพื่อเปลี่ยนความกว้างสำหรับฟิลด์เฉพาะ

ทำผ่าน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

ในความคิดของฉันนี่เป็นคำตอบที่ดีที่สุดเนื่องจากช่วยให้สามารถเปลี่ยนฟิลด์แต่ละฟิลด์ได้โดยไม่ต้องสร้างแบบฟอร์มใหม่
rbennell

21

ตัวเลือกที่รวดเร็วและสกปรกคือเพียงจัดเตรียมเทมเพลตที่กำหนดเองสำหรับรุ่นที่เป็นปัญหา

หากคุณสร้างเทมเพลตที่ชื่อadmin/<app label>/<class name>/change_form.htmlผู้ดูแลระบบจะใช้เทมเพลตนั้นแทนค่าเริ่มต้น นั่นคือถ้าคุณได้มีรูปแบบที่มีชื่อPersonใน app ชื่อที่คุณต้องการสร้างแม่แบบที่มีชื่อว่าpeopleadmin/people/person/change_form.html

เทมเพลตผู้ดูแลระบบมีextraheadบล็อกคุณสามารถแทนที่เพื่อสิ่งที่เกิดขึ้นใน<head>และชิ้นสุดท้ายของปริศนาคือความจริงที่ว่าทุกสาขามีรหัส HTML id_<field-name>ของ

ดังนั้นคุณสามารถใส่สิ่งต่อไปนี้ในเทมเพลตของคุณ:

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

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

ขอบคุณ! สิ่งนี้และคำตอบโดย alanjds มีประโยชน์มากสำหรับการเปลี่ยนเลย์เอาต์ในอินเทอร์เฟซผู้ดูแลระบบแบบต่อฟิลด์แทนที่จะเป็นแบบต่อฟิลด์
nealmcb

10

หากคุณต้องการเปลี่ยนแอตทริบิวต์ในอินสแตนซ์ต่อช่องคุณสามารถเพิ่มคุณสมบัติ "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 ซึ่งจะปรับช่องฟอร์ม แต่ละรายการคือทูเพิลของแอตทริบิวต์ที่คุณต้องการลบล้างและค่าที่คุณต้องการแทนที่ด้วย คุณสามารถป้อนแอตทริบิวต์ได้มากเท่าที่คุณต้องการตราบใดที่คุณแยกทูเพิลแต่ละรายการด้วยลูกน้ำ


IMO เป็นวิธีที่ดีที่สุดในการแก้ไขเพียงช่องเดียวและไม่ใช่ TextInput-widget ทั้งหมดในคราวเดียว (เหมือนคำตอบที่ยอมรับ)
ascripter

7

วิธีที่ดีที่สุดที่ฉันพบคือสิ่งนี้:

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 มากกว่าการแทนที่ฟิลด์ด้วยวิดเจ็ตที่แตกต่างกันเนื่องจากมันเก็บรักษาnameและhelp_textแอตทริบิวต์และค่าเริ่มต้นของฟิลด์โมเดลดังนั้นคุณไม่จำเป็นต้องคัดลอกลงในแบบฟอร์มของคุณ


7

ฉันมีปัญหาคล้ายกันกับ TextField ฉันใช้ Django 1.0.2 และต้องการเปลี่ยนค่าเริ่มต้นสำหรับ "แถว" ในพื้นที่ข้อความที่เกี่ยวข้อง 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 เมื่อกำหนดโมเดลของคุณ ฉันปรับเปลี่ยนจากคำตอบนี้เป็นคำถามที่คล้ายกัน


5

มีการอธิบายไว้อย่างดีในDjango FAQ :

ถาม:ฉันจะเปลี่ยนแอตทริบิวต์สำหรับวิดเจ็ตบนฟิลด์ในโมเดลของฉันได้อย่างไร

ตอบ:แทนที่ formfield_for_dbfield ในคลาส ModelAdmin / StackedInline / TabularInline

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)

เมื่อฉันใช้เทคนิคนี้ข้อความวิธีใช้จะไม่ปรากฏขึ้นและหากเป็น TabularInline ส่วนหัวของคอลัมน์จะกลายเป็น 'ไม่มี'
Steven T.Snyder

1
นี่ไม่ใช่แนวทางที่ดีเนื่องจากคุณกำลังล้างการตั้งค่าอื่น ๆ ทั้งหมดใน CharField ตัวอย่างเช่นตามค่าเริ่มต้นระบบจะตรวจจับอย่างชาญฉลาดว่าควรต้องใช้ฟิลด์แบบฟอร์มหรือไม่ แต่รหัสนี้ทำลายสิ่งนั้น คุณควรตั้งค่าวิดเจ็ตตามค่าที่ส่งคืนโดยการโทร super () ของคุณ
Cerin

5

คุณสามารถกำหนดขนาดฟิลด์ของคุณในสไตล์ชีตที่กำหนดเองได้ตลอดเวลาและบอกให้ Django ใช้สำหรับคลาส ModelAdmin ของคุณ:

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

คำตอบที่ดีที่สุด! ไม่มีการเล่นซอกับ formfields และแอตทริบิวต์ (และผลข้างเคียงที่เป็นไปได้) เพียงแค่ไฟล์ css ที่สามารถ - ด้วยรหัส / คลาสที่มีอยู่ในแบบฟอร์มผู้ดูแลระบบ django - กำหนดเป้าหมายเฉพาะบางฟิลด์หรือแม้แต่ทุกฟิลด์ได้อย่างง่ายดาย ขนาดใหญ่ขึ้น!
benzkji

2

สำหรับ 1.6 โดยใช้แบบฟอร์มฉันต้องระบุคุณลักษณะของ textarea ภายใน charfield:

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

1

คำตอบเดียวกับ 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)

1

นี่คือวิธีแก้ปัญหาที่เรียบง่าย แต่ยืดหยุ่น ใช้แบบฟอร์มที่กำหนดเองเพื่อแทนที่วิดเจ็ตบางตัว

# 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แอตทริบิวต์

วิธีการแก้ปัญหานี้ได้อธิบายไว้ในบทความนี้


0

หากคุณกำลังทำงานกับฟิลด์ ForeignKey ที่เกี่ยวข้องกับตัวเลือก / ตัวเลือก / เมนูแบบเลื่อนลงคุณสามารถแทนที่formfield_for_foreignkeyในอินสแตนซ์ผู้ดูแลระบบ:

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)

ข้อมูลเพิ่มเติมเกี่ยวกับรูปแบบนี้ที่นี่และที่นี่


-1

และอีกหนึ่งตัวอย่างเช่นกัน:

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

หากคุณต้องการแก้ไขเฉพาะขนาดช่องที่เจาะจงคุณสามารถใช้สิ่งนี้ได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.