ทำซ้ำชื่อฟิลด์และค่าของอินสแตนซ์โมเดลในเทมเพลต


183

ฉันพยายามสร้างแม่แบบพื้นฐานเพื่อแสดงค่าฟิลด์ของอินสแตนซ์ที่เลือกพร้อมกับชื่อของพวกเขา คิดว่ามันเป็นเพียงเอาต์พุตมาตรฐานของค่าของอินสแตนซ์นั้นในรูปแบบตารางโดยมีชื่อฟิลด์ (verbose_name โดยเฉพาะหากระบุไว้ในฟิลด์) ในคอลัมน์แรกและค่าของฟิลด์นั้นในคอลัมน์ที่สอง

ตัวอย่างเช่นสมมติว่าเรามีคำจำกัดความของโมเดลดังต่อไปนี้:

class Client(Model):
    name = CharField(max_length=150)
    email = EmailField(max_length=100, verbose_name="E-mail")

ฉันต้องการให้มันเป็นผลลัพธ์ในเทมเพลตเช่นนี้ (สมมติว่าเป็นอินสแตนซ์ที่มีค่าที่กำหนด):

Field Name      Field Value
----------      -----------
Name            Wayne Koorts
E-mail          waynes@email.com

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

<table>
    {% for field in fields %}
        <tr>
            <td>{{ field.name }}</td>
            <td>{{ field.value }}</td>
        </tr>
    {% endfor %}
</table>

มีวิธีที่ "เรียบร้อยแล้วอนุมัติ Django" หรือไม่? ดูเหมือนว่าเป็นงานที่ธรรมดามากและฉันจะต้องทำบ่อยๆสำหรับโครงการนี้โดยเฉพาะ

คำตอบ:


171

model._meta.get_all_field_names()จะให้ชื่อฟิลด์ของโมเดลทั้งหมดแก่คุณจากนั้นคุณสามารถใช้model._meta.get_field()เพื่อทำงานตามชื่อ verbose และgetattr(model_instance, 'field_name')เพื่อรับค่าจากโมเดล

หมายเหตุ: model._meta.get_all_field_names()เลิกใช้แล้วใน django 1.9 ใช้model._meta.get_fields()เพื่อรับฟิลด์ของโมเดลและfield.nameรับชื่อแต่ละฟิลด์แทน


2
คู่มือนี้ยังคงเป็นคู่มืออยู่มากและฉันจะต้องสร้างเมตาวัตถุบางอย่างในมุมมองที่ฉันส่งผ่านไปยังเทมเพลตซึ่งเป็นแฮ็คมากกว่าที่ฉันต้องการ แน่นอนว่าจะต้องมีวิธี neater?
Wayne Koorts

2
คุณสามารถแค็ปซูลทั้งหมดนี้ในชั้นเรียนเหมือน ModelForm ทำ
Ignacio Vazquez-Abrams

18
ฉันไม่เชื่อว่าคุณสามารถเรียกใช้เมธอด _ ใดก็ได้ในเทมเพลต
Issac Kelly

2
มันใช้งานได้ แต่คุณไม่ควรขึ้นอยู่กับ API ส่วนตัว (เพราะมันขึ้นต้นด้วย "_") เพื่อให้บรรลุ ปัญหาของการใช้ API ส่วนตัวก็คือวิธีการส่วนตัวนั้นไม่รับประกันว่าจะทำงานได้จากรุ่นหนึ่งไปยังอีกรุ่นหนึ่ง
Devy

1
ฉันคิดว่าวิธีนี้ไม่ควรเป็นที่ต้องการเนื่องจากเราไม่ควรเข้าถึงคุณลักษณะที่เริ่มต้นด้วยการขีดเส้นใต้จากเทมเพลต
GP92

72

คุณสามารถใช้ซีเรียลไลเซอร์คิวรี่คิวรี่เคียวรีเพื่อตั้งค่าของ Django ได้

เพียงใส่รหัสต่อไปนี้ในมุมมองของคุณ:

from django.core import serializers
data = serializers.serialize( "python", SomeModel.objects.all() )

แล้วในเทมเพลต:

{% for instance in data %}
    {% for field, value in instance.fields.items %}
        {{ field }}: {{ value }}
    {% endfor %}
{% endfor %}

ข้อได้เปรียบที่ยิ่งใหญ่ของมันคือความจริงที่ว่ามันจัดการกับความสัมพันธ์

สำหรับชุดย่อยของฟิลด์ลอง:

data = serializers.serialize('python', SomeModel.objects.all(), fields=('name','size'))

มันยอดเยี่ยม - แต่ด้วยวิธีนี้เราจะ จำกัด ผลลัพธ์ให้มีเพียงบางฟิลด์ได้อย่างไร
Herman Schaaf

2
นี่ควรเป็นคำตอบที่ชัดเจนจัดการกับกุญแจต่างประเทศและไม่มีการเรียก api ส่วนตัว คำตอบที่ดีขอบคุณ
Yunti

3
ไม่จำเป็นต้องใช้การทำให้เป็นอันดับ คุณสามารถใช้ค่า () วิธีการของชุดแบบสอบถามซึ่งส่งกลับพจนานุกรม นอกจากนี้วิธีนี้ยอมรับรายการของเขตข้อมูลที่จะย่อยใน ดูการเชื่อมโยง ดูคำตอบแบบเต็มของฉัน
3062149

เราสามารถอัปเดตสิ่งนี้เพื่อส่งในฟิลด์. แทนที่จะต้องประมวลผลแบบวนซ้ำได้หรือไม่? ฉันไม่ต้องการเปิดเผยชื่อรุ่น / ตาราง
Loser Coder

วิธีนี้อนุญาตให้verbose_nameส่งผ่านเขตข้อมูลได้หรือไม่
alias51

71

ในที่สุดก็พบทางออกที่ดีในเรื่องนี้ในรายชื่อผู้รับจดหมาย dev :

ในมุมมองเพิ่ม:

from django.forms.models import model_to_dict

def show(request, object_id):
    object = FooForm(data=model_to_dict(Foo.objects.get(pk=object_id)))
    return render_to_response('foo/foo_detail.html', {'object': object})

ในเทมเพลตเพิ่ม:

{% for field in object %}
    <li><b>{{ field.label }}:</b> {{ field.data }}</li>
{% endfor %}

1
วิธีแก้ปัญหาที่ดี แต่ไม่ธรรมดามากเพราะไม่ส่งคืน model_to_dict สำหรับเขตข้อมูล ForeignKey แต่เป็นผลลัพธ์แบบUnicodeดังนั้นคุณจึงไม่สามารถทำให้วัตถุที่ซับซ้อนเป็นอนุกรมได้อย่างง่ายดายกลายเป็น Dict
Vestel

22
การเขียนทับวัตถุอันตรายคุณควรใช้ชื่อ varaible อื่น
Emil Stenström

ขอบคุณ! ฉันแทนที่ model_to_dict () ของ Django เพื่อให้สามารถจัดการ ForeignKey ได้ โปรดดูคำตอบแยกต่างหากของฉัน (ฉันลบความคิดเห็นก่อนหน้าของฉันเนื่องจากความคิดเห็นไม่สนับสนุนการจัดรูปแบบโค้ดขออภัยฉันไม่รู้เช่นนั้น)
Magnus Gustavsson

2
สมมติว่าที่นี่FooFormเป็นแบบModelFormนั้นมันจะง่ายกว่าFooForm(instance=Foo.objects.get(pk=object_id)))ไหมถ้าจะทำ:
beruic

มีความคิดว่าคุณจะแสดงเฉพาะฟิลด์ที่แก้ไขได้ด้วยวิธีนี้ได้อย่างไร
alias51

22

ในแง่ของการเปิดตัว Django 1.8 (และการทำให้เป็นทางการของ Model _meta APIฉันคิดว่าฉันจะอัปเดตสิ่งนี้ด้วยคำตอบล่าสุด

สมมติว่าเป็นแบบเดียวกัน:

class Client(Model):
    name = CharField(max_length=150)
    email = EmailField(max_length=100, verbose_name="E-mail")

Django <= 1.7

fields = [(f.verbose_name, f.name) for f in Client._meta.fields]
>>> fields
[(u'ID', u'id'), (u'name', u'name'), (u'E-mail', u'email')]

Django 1.8+ (โมเดล _meta API อย่างเป็นทางการ)

เปลี่ยนเป็น Django 1.8:

Model _metaAPI มีอยู่เสมอภายในเป็น Django แต่ไม่มีเอกสารและรองรับอย่างเป็นทางการ เป็นส่วนหนึ่งของความพยายามที่จะทำให้ API นี้เป็นสาธารณะจุดเข้า API ที่มีอยู่แล้วบางส่วนมีการเปลี่ยนแปลงเล็กน้อย มีการให้คำแนะนำการย้ายข้อมูลเพื่อช่วยในการแปลงรหัสของคุณเพื่อใช้ API ใหม่อย่างเป็นทางการ

ในตัวอย่างด้านล่างเราจะใช้วิธีการที่เป็นทางการในการดึงอินสแตนซ์ฟิลด์ทั้งหมดของแบบจำลองผ่านClient._meta.get_fields():

fields = [(f.verbose_name, f.name) for f in Client._meta.get_fields()]
>>> fields
[(u'ID', u'id'), (u'name', u'name'), (u'E-mail', u'email')]

ที่จริงแล้วมันได้รับความสนใจของฉันว่าข้างต้นเล็กน้อยสำหรับสิ่งที่ต้องการ (ฉันเห็นด้วย!) เรียบง่ายดีกว่าซับซ้อน ฉันจะออกจากข้างต้นสำหรับการอ้างอิง อย่างไรก็ตามเพื่อแสดงในเทมเพลตวิธีที่ดีที่สุดคือใช้ ModelForm และส่งผ่านอินสแตนซ์ คุณสามารถวนซ้ำฟอร์ม (เทียบเท่าการวนซ้ำของแต่ละฟิลด์ของฟอร์ม) และใช้แอ็ตทริบิวต์ label เพื่อดึง verbose_name ของฟิลด์ model และใช้เมธอด value เพื่อดึงค่า:

from django.forms import ModelForm
from django.shortcuts import get_object_or_404, render
from .models import Client

def my_view(request, pk):
    instance = get_object_or_404(Client, pk=pk)
    
    class ClientForm(ModelForm):
        class Meta:
            model = Client
            fields = ('name', 'email')

    form = ClientForm(instance=instance)

    return render(
        request, 
        template_name='template.html',
        {'form': form}
    )

ตอนนี้เราแสดงฟิลด์ในเทมเพลต:

<table>
    <thead>
        {% for field in form %}
            <th>{{ field.label }}</th>
        {% endfor %}
    </thead>
    <tbody>
        <tr>
            {% for field in form %}
                <td>{{ field.value|default_if_none:'' }}</td>
            {% endfor %}
        </tr>
    </tbody>
</table>
 

2
มันจะดีถ้าคุณปรับคำตอบของคุณเพื่อแสดงวิธี "> 1.8" เพื่อใส่ฟิลด์โมเดลลงในเทมเพลต ในขณะนี้คำตอบของคุณไม่ตอบคำถามโดยตรง มันแสดงวิธีรับฟิลด์ของโมเดลในเชลล์
Escher

@Escher - อัปเดตคำตอบ! ขอบคุณสำหรับคำแนะนำ แจ้งให้เราทราบหากฉันพลาดอะไร / ทำอะไรผิดพลาด
Michael B

upvoted ฉันแก้ไขเพื่อรวมการพิมพ์ค่าเช่นเดียวกับชื่อฟิลด์ ดูว่าคุณคิดอย่างไร
Escher

คุณพิมพ์ค่าอยู่ที่ไหน ฉันเห็นเพียงพิมพ์ชื่อและ verbose_name หรือไม่
ดร. เออร์นี่

@MichaelB อืม ฉันไม่สามารถรับ "field.value" เพื่อทำงานได้ เขตข้อมูลปรากฏเป็นเขตข้อมูลฐานข้อมูลไม่ใช่ข้อมูลคอลัมน์จริง ฉันต้องใช้ตัวกรองที่เรียกว่า getattr (วัตถุชื่อ) Django เวอร์ชันใดที่เหมาะกับคุณ
ดร. เออร์นี่

19

นี่เป็นอีกวิธีหนึ่งโดยใช้วิธีการแบบ รุ่นนี้แก้ไขช่องตัวเลือก / ตัวเลือกข้ามเขตข้อมูลที่ว่างเปล่าและช่วยให้คุณสามารถยกเว้นเขตข้อมูลที่เฉพาะเจาะจง

def get_all_fields(self):
    """Returns a list of all field names on the instance."""
    fields = []
    for f in self._meta.fields:

        fname = f.name        
        # resolve picklists/choices, with get_xyz_display() function
        get_choice = 'get_'+fname+'_display'
        if hasattr(self, get_choice):
            value = getattr(self, get_choice)()
        else:
            try:
                value = getattr(self, fname)
            except AttributeError:
                value = None

        # only display fields with values and skip some fields entirely
        if f.editable and value and f.name not in ('id', 'status', 'workshop', 'user', 'complete') :

            fields.append(
              {
               'label':f.verbose_name, 
               'name':f.name, 
               'value':value,
              }
            )
    return fields

จากนั้นในเทมเพลตของคุณ:

{% for f in app.get_all_fields %}
  <dt>{{f.label|capfirst}}</dt>
    <dd>
      {{f.value|escape|urlize|linebreaks}}
    </dd>
{% endfor %}

3
ทำไมคุณต้องการexcept User.DoesNotExist:?
เจ็ด

ฉันอยากจะใช้ AttributeError แทนที่จะเป็น User.DoesNotExist - ฉันไม่เห็นว่าทำไมมันถึงทิ้ง User.DoesNotExist
askvictor

นอกจากนี้อาจเป็นการดีกว่าที่จะใช้ self._meta.get_fields () เนื่องจากมีการเปิดเผยอย่างเป็นทางการใน django 1.8+ อย่างไรก็ตามคุณจะจบลงด้วยความสัมพันธ์ในรหัสซึ่งคุณจะต้องกรองออกโดยตรวจสอบ f.is_relation
askvictor

ฉันได้แก้ไขคำตอบเพื่อใช้ AttributeError แทน User.DoesNotExist (ซึ่งเป็นสิ่งที่เหลือจากการใช้งานดั้งเดิมของฉัน) ขอบคุณ ฉันถือ_meta.get_fields()จนกว่าฉันจะสามารถทดสอบได้
shacker

13

ตกลงฉันรู้ว่ามันสายไปหน่อย แต่เนื่องจากฉันพบสิ่งนี้ก่อนพบคำตอบที่ถูกต้องดังนั้นอาจมีคนอื่น

จากdjango docs :

# This list contains a Blog object.
>>> Blog.objects.filter(name__startswith='Beatles')
[<Blog: Beatles Blog>]

# This list contains a dictionary.
>>> Blog.objects.filter(name__startswith='Beatles').values()
[{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]

ฉันชอบคำตอบนี้ หากแบบสอบถามของคุณส่งคืนมากกว่าหนึ่งระเบียนและคุณต้องการเพียงระเบียนล่าสุดให้ทำดังต่อไปนี้ 1. ให้แน่ใจว่าคุณมีordering = ['-id']ในของวัตถุในclass Meta: models.py2. จากนั้นใช้Blog.objects.filter(name__startswith='Beatles').values()[0]
เจ็ด

ความคิดที่ฉลาด แต่ถ้าคุณมีmodelวัตถุอยู่แล้วคุณจะต้องตีฐานข้อมูลอีกครั้งเพื่อรับฟิลด์ มีวิธีไหนบ้าง?
frnhr

@ user1763732 เพียงตรวจสอบเอกสารประกอบสำหรับ QuerySet: docs.djangoproject.com/th/dev/ref/models/querysets
olofom

9

คุณสามารถใช้values()เมธอด a querysetซึ่งส่งคืนพจนานุกรม นอกจากนี้วิธีนี้ยอมรับรายการของเขตข้อมูลที่จะย่อยใน values()วิธีจะไม่ทำงานกับget()ดังนั้นคุณต้องใช้filter()(ดูที่ชุดข้อความ API )

ในview...

def show(request, object_id):
   object = Foo.objects.filter(id=object_id).values()[0]
   return render_to_response('detail.html', {'object': object})

ในdetail.html...

<ul>
   {% for key, value in object.items %}
        <li><b>{{ key }}:</b> {{ value }}</li>
   {% endfor %}
</ul>

สำหรับชุดของอินสแตนซ์ที่ส่งคืนโดยตัวกรอง:

   object = Foo.objects.filter(id=object_id).values() # no [0]

ในdetail.html ...

{% for instance in object %}
<h1>{{ instance.id }}</h1>
<ul>
    {% for key, value in instance.items %}
        <li><b>{{ key }}:</b>  {{ value }}</li>
    {% endfor %}
</ul>
{% endfor %}

นี่มันสุดยอดมากขอบคุณ! ฉันมีคำถามถ้าคุณสามารถช่วยฉันด้วย; ฉันวางวัตถุข้อมูลในtableดังนั้นฉันต้องการแต่ละkeys thใน ฉันจะทำอย่างนั้นได้อย่างไรโดยไม่ต้องวนซ้ำ? ใช้อินสแตนซ์ของวัตถุใด ๆ แล้ววนซ้ำไปเรื่อย ๆ เพื่อมันkey? ขณะนี้ฉันกำลังแยกผ่านmodel_to_dict(Model())สำหรับthแต่ฉันคิดว่ามันเป็น instantiation วัตถุที่ไม่จำเป็น
Oxwivi

คำตอบที่ยอดเยี่ยม ส่วนตัวผมใช้สิ่งนี้ทั้งในมุมมองรายการและในมุมมองรายละเอียด มุมมองรายการมีความชัดเจนในการใช้งาน แต่มีมุมมองรายละเอียดฉันแทนที่get_objectในมุมมองรายละเอียด (mangled เนื่องจากข้อ จำกัด ของโค้ดอินไลน์ในความคิดเห็นและไม่รู้สึกว่ามันเพียงพอสำหรับคำตอบของตัวเองพิจารณาว่าอิ่มตัวกระทู้นี้อย่างไร): def get_object(self, **kwargs): obj = super().get_object(**kwargs) obj = obj.__class__.objects.filter(pk=obj.pk).values()[0] return obj
sdconrox

คุณจะเพิ่มobj.get_absolute_urlรายการนี้โดยไม่ทำแถวซ้ำได้อย่างไร?
alias51

8

ฉันใช้https://stackoverflow.com/a/3431104/2022534แต่แทนที่ model_to_dict () ของ Django ด้วยสิ่งนี้เพื่อให้สามารถจัดการ ForeignKey ได้:

def model_to_dict(instance):
    data = {}
    for field in instance._meta.fields:
        data[field.name] = field.value_from_object(instance)
        if isinstance(field, ForeignKey):
            data[field.name] = field.rel.to.objects.get(pk=data[field.name])
    return data

โปรดทราบว่าฉันได้ทำให้มันง่ายขึ้นเล็กน้อยโดยการลบส่วนต่าง ๆ ของต้นฉบับที่ฉันไม่ต้องการออก คุณอาจต้องการที่จะนำกลับมา


8

คุณสามารถมีแบบฟอร์มทำงานให้คุณ

def my_model_view(request, mymodel_id):
    class MyModelForm(forms.ModelForm):
        class Meta:
            model = MyModel

    model = get_object_or_404(MyModel, pk=mymodel_id)
    form = MyModelForm(instance=model)
    return render(request, 'model.html', { 'form': form})

จากนั้นในเทมเพลต:

<table>
    {% for field in form %}
        <tr>
            <td>{{ field.name }}</td>
            <td>{{ field.value }}</td>
        </tr>
    {% endfor %}
</table>

3
วิธีนี้ (ใช้ภายใน a DetailView) ใช้ได้ดีสำหรับฉัน แต่คุณอาจต้องการที่จะใช้แทนfield.label field.name
David Cain

7

ควรมีวิธีการทำเช่นนี้ในตัว ผมเขียนอรรถประโยชน์นี้build_pretty_data_viewที่ใช้วัตถุและรูปแบบเช่นแบบฟอร์ม (แบบฟอร์มตามรูปแบบของคุณ) SortedDictและผลตอบแทน

ประโยชน์ของการแก้ปัญหานี้รวมถึง:

  • มันรักษาคำสั่งซื้อโดยใช้ Django ในSortedDictตัว
  • เมื่อพยายามรับเลเบล / verbose_name แต่กลับไปที่ชื่อฟิลด์หากไม่ได้กำหนดไว้
  • นอกจากนี้ยังจะเลือกexclude()รายการชื่อเขตข้อมูลเพื่อแยกเขตข้อมูลบางอย่าง
  • หากคลาสฟอร์มของคุณมี a Meta: exclude()แต่คุณยังต้องการคืนค่าให้เพิ่มเขตข้อมูลเหล่านั้นในappend()รายการ ตัวเลือก

ที่จะใช้วิธีการแก้ปัญหานี้เป็นครั้งแรกเพิ่มไฟล์นี้ / views.pyฟังก์ชั่นที่ใดที่หนึ่งแล้วนำเข้าของคุณ

utils.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# vim: ai ts=4 sts=4 et sw=4
from django.utils.datastructures import SortedDict


def build_pretty_data_view(form_instance, model_object, exclude=(), append=()):
    i=0
    sd=SortedDict()

    for j in append:
        try:
            sdvalue={'label':j.capitalize(),
                     'fieldvalue':model_object.__getattribute__(j)}
            sd.insert(i, j, sdvalue)
            i+=1
        except(AttributeError):
            pass

    for k,v in form_instance.fields.items():
        sdvalue={'label':"", 'fieldvalue':""}
        if not exclude.__contains__(k):
            if v.label is not None:
                sdvalue = {'label':v.label,
                           'fieldvalue': model_object.__getattribute__(k)}
            else:
                sdvalue = {'label':k,
                           'fieldvalue': model_object.__getattribute__(k)}
            sd.insert(i, k, sdvalue)
            i+=1
    return sd

ดังนั้นในตัวคุณviews.pyคุณอาจทำอะไรแบบนี้

from django.shortcuts import render_to_response
from django.template import RequestContext
from utils import build_pretty_data_view
from models import Blog
from forms import BlogForm
.
.
def my_view(request):
   b=Blog.objects.get(pk=1)
   bf=BlogForm(instance=b)
   data=build_pretty_data_view(form_instance=bf, model_object=b,
                        exclude=('number_of_comments', 'number_of_likes'),
                        append=('user',))

   return render_to_response('my-template.html',
                          RequestContext(request,
                                         {'data':data,}))

ตอนนี้ในmy-template.htmlเทมเพลตของคุณคุณสามารถวนซ้ำข้อมูลเช่นนี้ ...

{% for field,value in data.items %}

    <p>{{ field }} : {{value.label}}: {{value.fieldvalue}}</p>

{% endfor %}

โชคดี. หวังว่านี่จะช่วยใครซักคน!


7

ด้านล่างนี้เป็นของเราแรงบันดาลใจจากของ get_all_fieldsshacker มันได้รับ dict หนึ่งอินสแตนซ์ของโมเดลถ้าพบความสัมพันธ์กับฟิลด์จากนั้นกำหนดค่าฟิลด์ที่ dict ซ้ำ

def to_dict(obj, exclude=[]):
    """生成一个 dict, 递归包含一个 model instance 数据.
    """
    tree = {}
    for field in obj._meta.fields + obj._meta.many_to_many:
        if field.name in exclude or \
           '%s.%s' % (type(obj).__name__, field.name) in exclude:
            continue

        try :
            value = getattr(obj, field.name)
        except obj.DoesNotExist:
            value = None

        if type(field) in [ForeignKey, OneToOneField]:
            tree[field.name] = to_dict(value, exclude=exclude)
        elif isinstance(field, ManyToManyField):
            vs = []
            for v in value.all():
                vs.append(to_dict(v, exclude=exclude))
            tree[field.name] = vs
        elif isinstance(field, DateTimeField):
            tree[field.name] = str(value)
        elif isinstance(field, FileField):
            tree[field.name] = {'url': value.url}
        else:
            tree[field.name] = value

    return tree

ฟังก์ชั่นนี้ส่วนใหญ่จะใช้ในการถ่ายโอนข้อมูลตัวอย่างแบบจำลองไปยังข้อมูล json:

def to_json(self):
    tree = to_dict(self, exclude=('id', 'User.password'))
    return json.dumps(tree, ensure_ascii=False)

การทำงานที่ดี! แนะนำการเพิ่มตัวเลือกสนับสนุน ... elif hasattr (ฟิลด์, 'options'): tree [field.name] = dict (field.choices) .get (ค่า, ค่า)
oden

5

แทนที่จะแก้ไขทุกรุ่นฉันขอแนะนำให้เขียนเทมเพลตหนึ่งแท็กซึ่งจะส่งคืนฟิลด์ทั้งหมดของโมเดลที่ระบุ วัตถุทุกคนมีรายการของเขตข้อมูล
วัตถุทุกฟิลด์มีคุณสมบัติที่จะส่งคืนชื่อและวิธีการที่มาพร้อมกับแบบจำลองของคุณจะคืนค่าของมัน ส่วนที่เหลือเป็นง่ายๆเป็นก็กล่าวในเอกสาร Django._meta.fields
namevalue_to_string()object

นี่คือตัวอย่างของฉันว่าเทมเพลทแท็กนี้อาจมีลักษณะอย่างไร:

    from django.conf import settings
    from django import template

    if not getattr(settings, 'DEBUG', False):
        raise template.TemplateSyntaxError('get_fields is available only when DEBUG = True')


    register = template.Library()

    class GetFieldsNode(template.Node):
        def __init__(self, object, context_name=None):
            self.object = template.Variable(object)
            self.context_name = context_name

        def render(self, context):
            object = self.object.resolve(context)
            fields = [(field.name, field.value_to_string(object)) for field in object._meta.fields]

            if self.context_name:
                context[self.context_name] = fields
                return ''
            else:
                return fields


    @register.tag
    def get_fields(parser, token):
        bits = token.split_contents()

        if len(bits) == 4 and bits[2] == 'as':
            return GetFieldsNode(bits[1], context_name=bits[3])
        elif len(bits) == 2:
            return GetFieldsNode(bits[1])
        else:
            raise template.TemplateSyntaxError("get_fields expects a syntax of "
                           "{% get_fields <object> [as <context_name>] %}")

4

ใช่มันไม่สวยคุณจะต้องทำเสื้อคลุมของคุณเอง ลองดูที่แอพพลิเคชั่นdatabrowseซึ่งมีฟังก์ชั่นการใช้งานทั้งหมดที่คุณต้องการจริงๆ


ฉันจะบอกว่า .... databrowse ทำอย่างนั้นแม้ว่าฉันจะพบว่ามันเป็นแอพที่ไร้ประโยชน์อย่างสมบูรณ์
mpen

4

สิ่งนี้อาจถูกพิจารณาว่าเป็นการแฮ็ค แต่ฉันได้ทำสิ่งนี้มาก่อนใช้ modelform_factory เพื่อเปลี่ยนอินสแตนซ์ของโมเดลเป็นรูปแบบ

คลาสแบบฟอร์มมีข้อมูลเพิ่มเติมมากมายภายในซึ่งง่ายมากที่จะวนซ้ำไปมาและมันจะตอบสนองวัตถุประสงค์เดียวกันโดยเสียค่าใช้จ่ายเล็กน้อย หากขนาดชุดของคุณค่อนข้างเล็กฉันคิดว่าผลกระทบต่อประสิทธิภาพจะไม่สำคัญ

ข้อดีอย่างหนึ่งที่นอกเหนือจากความสะดวกสบายของหลักสูตรคือคุณสามารถเปลี่ยนตารางเป็น DataGrid ที่แก้ไขได้ในภายหลัง


4

ฉันคิดวิธีต่อไปนี้ซึ่งเหมาะกับฉันเพราะในทุกกรณีโมเดลจะมี ModelForm ที่เชื่อมโยงกับมัน

def GetModelData(form, fields):
    """
    Extract data from the bound form model instance and return a
    dictionary that is easily usable in templates with the actual
    field verbose name as the label, e.g.

    model_data{"Address line 1": "32 Memory lane",
               "Address line 2": "Brainville",
               "Phone": "0212378492"}

    This way, the template has an ordered list that can be easily
    presented in tabular form.
    """
    model_data = {}
    for field in fields:
        model_data[form[field].label] = eval("form.data.%s" % form[field].name)
    return model_data

@login_required
def clients_view(request, client_id):
    client = Client.objects.get(id=client_id)
    form = AddClientForm(client)

    fields = ("address1", "address2", "address3", "address4",
              "phone", "fax", "mobile", "email")
    model_data = GetModelData(form, fields)

    template_vars = RequestContext(request,
        {
            "client": client,
            "model_data": model_data
        }
    )
    return render_to_response("clients-view.html", template_vars)

นี่คือสารสกัดจากแม่แบบที่ฉันใช้สำหรับมุมมองนี้:

<table class="client-view">
    <tbody>
    {% for field, value in model_data.items %}
        <tr>
            <td class="field-name">{{ field }}</td><td>{{ value }}</td>
        </tr>
    {% endfor %}
    </tbody>
</table>

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

ฉันจะไม่ยอมรับสิ่งนี้เป็นคำตอบเพราะฉันมั่นใจว่ามีบางคนสามารถคิดอะไรเพิ่มเติมเกี่ยวกับ "Djangonic" :-)

อัปเดต:ฉันเลือกคำตอบนี้เป็นคำตอบสุดท้ายเพราะเป็นคำตอบที่ง่ายที่สุดที่ฉันต้องการ ขอบคุณทุกคนที่ให้คำตอบ


3

Django 1.7 ทางออกสำหรับฉัน:

มีตัวแปรตรงกับคำถาม แต่คุณควรแยกตัวอย่างนี้ออก

กุญแจสำคัญในที่นี้คือการใช้.__dict__โมเดล
views.py :

def display_specific(request, key):
  context = {
    'question_id':question_id,
    'client':Client.objects.get(pk=key).__dict__,
  }
  return render(request, "general_household/view_specific.html", context)

เทมเพลต :

{% for field in gen_house %}
    {% if field != '_state' %}
        {{ gen_house|getattribute:field }}
    {% endif %}
{% endfor %}

ในแม่แบบฉันใช้ตัวกรองเพื่อเข้าถึงฟิลด์ใน dict
filters.py :

@register.filter(name='getattribute')
def getattribute(value, arg):
  if value is None or arg is None:
    return ""
  try:
    return value[arg]
  except KeyError:
    return ""
  except TypeError:
    return ""

2

ฉันใช้นี้https://github.com/miracle2k/django-tables

<table>
<tr>
    {% for column in table.columns %}
    <th><a href="?sort={{ column.name_toggled }}">{{ column }}</a></th>
    {% endfor %}
</tr>
{% for row in table.rows %}
    <tr>
    {% for value in row %}
        <td>{{ value }}</td>
    {% endfor %}
    </tr>
{% endfor %}
</table>

2

วิธีนี้แสดงวิธีใช้คลาสเช่น ModelForm ของ django และเทมเพลตแท็กเช่น {{form.as_table}} แต่ให้ตารางทั้งหมดมีลักษณะเหมือนเอาต์พุตข้อมูลไม่ใช่ฟอร์ม

ขั้นตอนแรกคือการวิดเจ็ต TextInput ของคลาสย่อย django:

from django import forms
from django.utils.safestring import mark_safe
from django.forms.util import flatatt

class PlainText(forms.TextInput):
    def render(self, name, value, attrs=None):
        if value is None:
            value = ''
        final_attrs = self.build_attrs(attrs)
        return mark_safe(u'<p %s>%s</p>' % (flatatt(final_attrs),value))

จากนั้นฉันก็จัดคลาสย่อยของ Modelango ของ django เพื่อสลับวิดเจ็ตเริ่มต้นสำหรับรุ่นอ่านอย่างเดียว:

from django.forms import ModelForm

class ReadOnlyModelForm(ModelForm):
    def __init__(self,*args,**kwrds):
        super(ReadOnlyModelForm,self).__init__(*args,**kwrds)
        for field in self.fields:
            if isinstance(self.fields[field].widget,forms.TextInput) or \
               isinstance(self.fields[field].widget,forms.Textarea):
                self.fields[field].widget=PlainText()
            elif isinstance(self.fields[field].widget,forms.CheckboxInput):
                self.fields[field].widget.attrs['disabled']="disabled" 

นั่นเป็นเพียงเครื่องมือเดียวที่ฉันต้องการ แต่ไม่ควรขยายความคิดนี้ไปยังวิดเจ็ตอื่น


1

เพียงแก้ไข @wonder

def to_dict(obj, exclude=[]):
    tree = {}
    for field in obj._meta.fields + obj._meta.many_to_many:
        if field.name in exclude or \
           '%s.%s' % (type(obj).__name__, field.name) in exclude:
            continue
        try :
            value = getattr(obj, field.name)
        except obj.DoesNotExist as e:
            value = None
        except ObjectDoesNotExist as e:
            value = None
            continue
        if type(field) in [ForeignKey, OneToOneField]:
            tree[field.name] = to_dict(value, exclude=exclude)
        elif isinstance(field, ManyToManyField):
            vs = []
            for v in value.all():
                vs.append(to_dict(v, exclude=exclude))
            tree[field.name] = vs
        else:
            tree[field.name] = obj.serializable_value(field.name)
    return tree

ให้ Django จัดการฟิลด์อื่นทั้งหมดนอกเหนือจากฟิลด์ที่เกี่ยวข้อง ฉันรู้สึกว่ามีเสถียรภาพมากขึ้น



0

ฉันเพิ่งทดสอบบางอย่างเช่นนี้ในเปลือกและดูเหมือนว่าจะทำงาน:

my_object_mapped = {attr.name: str(getattr(my_object, attr.name)) for attr in MyModel._meta.fields}

โปรดทราบว่าหากคุณต้องการให้ตัวแทน str () สำหรับวัตถุต่างประเทศคุณควรกำหนดไว้ในวิธีการของพวกเขาstr จากนั้นคุณมีค่าสำหรับวัตถุ จากนั้นคุณสามารถแสดงเทมเพลตหรืออะไรก็ได้


0

Django> = 2.0

เพิ่มget_fields()ของคุณmodels.py:

class Client(Model):
    name = CharField(max_length=150)
    email = EmailField(max_length=100, verbose_name="E-mail")

    def get_fields(self):
        return [(field.verbose_name, field.value_from_object(self)) for field in self.__class__._meta.fields]

จากนั้นก็เรียกว่าเป็นobject.get_fieldsของคุณtemplate.html:

<table>
    {% for label, value in object.get_fields %}
        <tr>
            <td>{{ label }}</td>
            <td>{{ value }}</td>
        </tr>
    {% endfor %}
</table>

-1

<table border='1'>
	<tr>
		{% for mfild in fields%}
			<td>{{mfild}}</td>
		{% endfor%}
	</tr>
    {%for v in records%}
        <tr>
        	<td>{{v.id}}</td>
        	<td>{{v.title}}</td>
        	<td class="">{{v.desc}}</td>

        </tr>

    {% endfor%}
 </table>
 
 
enter code here


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