Itérer sur les noms et valeurs des champs d'instance de modèle dans le modèle


183

J'essaie de créer un modèle de base pour afficher les valeurs de champ de l'instance sélectionnée, ainsi que leurs noms. Considérez-le simplement comme une sortie standard des valeurs de cette instance au format table, avec le nom du champ (verbose_name spécifiquement s'il est spécifié sur le champ) dans la première colonne et la valeur de ce champ dans la deuxième colonne.

Par exemple, disons que nous avons la définition de modèle suivante:

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

Je voudrais qu'il soit affiché dans le modèle comme ceci (supposons une instance avec les valeurs données):

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

Ce que j'essaie de réaliser, c'est de pouvoir transmettre une instance du modèle à un modèle et de pouvoir l'itérer dynamiquement dans le modèle, quelque chose comme ceci:

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

Existe-t-il une manière "approuvée par Django" de faire cela? Cela semble être une tâche très courante, et je devrai le faire souvent pour ce projet particulier.

Réponses:


171

model._meta.get_all_field_names()vous donnera tous les noms de champ du modèle, puis vous pourrez l'utiliser pour accéder au model._meta.get_field()nom détaillé et getattr(model_instance, 'field_name')pour obtenir la valeur du modèle.

REMARQUE: model._meta.get_all_field_names()est obsolète dans django 1.9. Utilisez plutôt model._meta.get_fields()pour obtenir les champs du modèle et field.namepour obtenir chaque nom de champ.


2
C'est encore très manuel, et je devrais créer une sorte de méta-objet dans la vue que je passe ensuite dans le modèle, ce qui est plus un hack que je ne le souhaiterais. Il doit sûrement y avoir un moyen plus soigné?
Wayne Koorts

2
Vous pouvez encapsuler tout cela dans une classe, un peu comme le fait ModelForm.
Ignacio Vazquez-Abrams

18
Je ne pense pas que vous puissiez appeler des méthodes _ dans les modèles.
Issac Kelly

2
Cela fonctionne mais vous ne devriez pas dépendre d'une API privée (car elle préfixe "_") pour y parvenir. Le problème avec le recours à une API privée est que les méthodes privées ne sont pas garanties de fonctionner d'une version à l'autre.
Devy

1
Je pense que cette méthode ne devrait pas être préférée car nous ne devrions pas accéder aux attributs commençant par un trait de soulignement à partir de modèles
GP92

72

Vous pouvez utiliser le sérialiseur de jeu de requêtes to-python de Django.

Mettez simplement le code suivant dans votre vue:

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

Et puis dans le modèle:

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

Son grand avantage est le fait qu'il gère les champs de relation.

Pour le sous-ensemble de champs, essayez:

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

C'est cool - mais avec cette méthode, comment limiterait-on les résultats à certains champs seulement?
Herman Schaaf

2
Cela devrait être la réponse définitive, gère les clés étrangères et aucun appel d'API privé. Excellente réponse, merci.
Yunti

3
Il n'est pas nécessaire d'utiliser serialize. Vous pouvez utiliser la méthode values ​​() d'un ensemble de requêtes , qui renvoie un dictionnaire. En outre, cette méthode accepte une liste de champs à sous-ensembles. Voir le lien . Voir ma réponse complète.
user3062149

Pouvons-nous mettre à jour cela pour n'envoyer que les .fields au lieu d'avoir à le traiter en boucle? Je ne veux pas exposer les noms des modèles / tables
Loser Coder

Cette méthode permet-elle verbose_namede passer le champ du?
alias51 le

71

Enfin trouvé une bonne solution à cela sur la liste de diffusion des développeurs :

Dans la vue, ajoutez:

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})

dans le modèle, ajoutez:

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

1
Bonne solution, mais pas très générique car elle renvoie non pas un model_to_dict pour les champs ForeignKey, mais un résultat unicode , donc vous ne pouvez pas facilement sérialiser un objet complexe dans dict
Vestel

22
Objet dangereux à remplacer, vous devez utiliser un autre nom variable.
Emil Stenström

Je vous remercie! J'ai remplacé le model_to_dict () de Django pour pouvoir gérer ForeignKey. Veuillez consulter ma réponse séparée (j'ai supprimé mon commentaire précédent car les commentaires ne prennent pas en charge le formatage du code. Désolé, je ne le savais pas.)
Magnus Gustavsson

2
En supposant ici que FooFormc'est un ModelForm, ne serait-il pas plus facile de simplement faire FooForm(instance=Foo.objects.get(pk=object_id))):?
beruic

Une idée de la façon dont vous n'afficheriez que les champs modifiables avec cette méthode?
alias51

22

À la lumière de la sortie de Django 1.8 (et de la formalisation de l' API Model _meta , j'ai pensé que je mettrais à jour cela avec une réponse plus récente.

En supposant le même modèle:

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+ (API modèle _meta formalisée)

Modifié dans Django 1.8:

Le modèle _meta API a toujours existé en tant que Django interne, mais n'a pas été formellement documentée et prise en charge. Dans le cadre des efforts visant à rendre cette API publique, certains des points d'entrée d'API déjà existants ont légèrement changé. Un guide de migration a été fourni pour vous aider à convertir votre code pour utiliser la nouvelle API officielle.

Dans l'exemple ci-dessous, nous utiliserons la méthode formalisée pour récupérer toutes les instances de champ d'un modèle viaClient._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')]

En fait, il a été porté à mon attention que ce qui précède est légèrement exagéré pour ce qui était nécessaire (je suis d'accord!). Le simple vaut mieux que le complexe. Je laisse ce qui précède pour référence. Cependant, pour l'afficher dans le modèle, la meilleure méthode serait d'utiliser un ModelForm et de transmettre une instance. Vous pouvez parcourir le formulaire (équivalent de l'itération sur chacun des champs du formulaire) et utiliser l'attribut label pour récupérer le verbose_name du champ de modèle et utiliser la méthode value pour récupérer la valeur:

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}
    )

Maintenant, nous rendons les champs dans le modèle:

<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
Ce serait formidable si vous ajustez votre réponse pour montrer la manière "> 1.8" de placer les champs de modèle dans un modèle. Pour le moment, votre réponse ne répond pas directement à la question; il montre comment obtenir les champs du modèle dans le shell.
Escher

@Escher - Mise à jour de la réponse! Merci pour la suggestion. Faites-moi savoir si j'ai manqué quelque chose / tout gâché
Michael B

Vote positif. J'ai modifié pour inclure l'impression des valeurs ainsi que des noms de champ. Voyez ce que vous pensez.
Escher

Où imprimez-vous les valeurs? Je le vois seulement imprimer le nom et le verbose_name?
Dr.Ernie

@MichaelB Hmm. Je n'ai pas pu faire fonctionner "field.value"; les champs semblent être des champs de base de données, pas les données de colonne réelles. J'ai dû utiliser un filtre qui appelait getattr (objet, nom). Quelle version de Django cela fonctionne pour vous?
Dr Ernie

19

Voici une autre approche utilisant une méthode modèle. Cette version résout les champs de liste de sélection / choix, ignore les champs vides et vous permet d'exclure des champs spécifiques.

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

Puis dans votre modèle:

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

3
pourquoi avez-vous besoin du except User.DoesNotExist:?
Sevenearths

Je serais enclin à utiliser AttributeError au lieu de User.DoesNotExist - Je ne vois pas pourquoi cela lancerait User.DoesNotExist.
askvictor

De plus, il serait peut-être préférable d'utiliser self._meta.get_fields () car cela est officiellement exposé dans django 1.8+. Cependant, vous vous retrouvez avec des relations dans le code, que vous devrez filtrer en vérifiant f.is_relation
askvictor

J'ai modifié la réponse pour utiliser AttributeError au lieu de User.DoesNotExist (qui était un reste de mon implémentation d'origine). Merci. Je tiens bon _meta.get_fields()jusqu'à ce que je puisse le tester.
shacker

13

Ok, je sais que c'est un peu tard, mais comme je suis tombé dessus avant de trouver la bonne réponse, quelqu'un d'autre pourrait le faire.

À partir de la documentation django :

# 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.'}]

J'aime cette réponse. Si votre requête renvoie plusieurs enregistrements et que vous souhaitez uniquement le dernier, procédez comme suit. 1. Assurez-vous que vous avez ordering = ['-id']dans class Meta:votre objet models.py. 2. puis utilisezBlog.objects.filter(name__startswith='Beatles').values()[0]
Sevenearths

Idée brillante. Mais si vous avez déjà un modelobjet, vous allez à nouveau frapper la base de données juste pour obtenir les champs. Un moyen de contourner cela?
frnhr

@ user1763732 vérifiez simplement les documentations du QuerySet: docs.djangoproject.com/en/dev/ref/models/querysets
olofom

9

Vous pouvez utiliser la values()méthode de a queryset, qui renvoie un dictionnaire. En outre, cette méthode accepte une liste de champs à sous-ensembles. La values()méthode ne fonctionnera pas avec get(), vous devez donc utiliser filter()(reportez-vous à l' API QuerySet ).

Dans view...

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

Dans detail.html...

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

Pour une collection d'instances renvoyées par le filtre:

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

En détail.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 %}

C'est incroyable, merci! J'ai une question, si vous pouvez m'aider; Je mets toutes les données d'objets dans un table, donc j'ai besoin de chaque keys dans un th. Comment faire cela sans boucles? Prenez n'importe quelle instance d'objet et parcourez-la pendant keys? Actuellement, je passe séparément model_to_dict(Model())pour le th, mais je pense que c'est une instanciation d'objet inutile.
Oxwivi

Réponse fantastique. Personnellement, je l'ai utilisé à la fois dans une vue de liste et dans une vue de détail. La vue de liste est en grande partie évidente à implémenter, mais avec la vue détaillée, je remplace get_objectla vue détaillée (mutilée en raison de la limitation du code en ligne sur les commentaires, et je ne pense pas que cela soit suffisant pour sa propre réponse compte tenu de la saturation de ce fil): def get_object(self, **kwargs): obj = super().get_object(**kwargs) obj = obj.__class__.objects.filter(pk=obj.pk).values()[0] return obj
sdconrox

comment ajouteriez-vous obj.get_absolute_urlà cette liste sans dupliquer les lignes?
alias51

8

J'ai utilisé https://stackoverflow.com/a/3431104/2022534 mais j'ai remplacé le model_to_dict () de Django par ceci pour pouvoir gérer 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

Veuillez noter que je l'ai simplifié un peu en supprimant les parties de l'original dont je n'avais pas besoin. Vous voudrez peut-être les remettre.


8

Vous pouvez demander à un formulaire de faire le travail pour vous.

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})

Puis dans le modèle:

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

3
Cette méthode (employée dans a DetailView) fonctionne bien pour moi. Cependant, vous souhaiterez peut-être utiliser à la field.labelplace de field.name.
David Cain

7

Il devrait vraiment y avoir un moyen intégré de le faire. J'ai écrit cet utilitaire build_pretty_data_viewqui prend un objet modèle et une instance de formulaire (un formulaire basé sur votre modèle) et renvoie unSortedDict .

Les avantages de cette solution incluent:

  • Il préserve l'ordre en utilisant la fonction intégrée de Django SortedDict.
  • Quand essaie d'obtenir l'étiquette / verbose_name, mais revient au nom du champ si l'un n'est pas défini.
  • Il faudra également éventuellement un exclude() liste de noms de champs pour exclure certains champs.
  • Si votre classe de formulaire inclut un Meta: exclude(), mais que vous souhaitez toujours renvoyer les valeurs, ajoutez ces champs à la append()liste facultative .

Pour utiliser cette solution, ajoutez d'abord ce fichier / cette fonction quelque part, puis importez-le dans votre fichier 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

Alors maintenant, views.pytu pourrais faire quelque chose comme ça

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,}))

Maintenant, dans votre my-template.htmlmodèle, vous pouvez parcourir les données comme ceci ...

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

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

{% endfor %}

Bonne chance. J'espère que cela aide quelqu'un!


7

Ci-dessous est le mien, inspiré de shacker get_all_fields . Il obtient un dict d'une instance de modèle, si le champ de relation rencontre, puis attribue récursivement la valeur du champ à un 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

Cette fonction est principalement utilisée pour vider une instance de modèle dans des données json:

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

Bon travail! Suggérer l'ajout de choix de support ... elif hasattr (champ, 'choix'): tree [field.name] = dict (field.choices) .get (valeur, valeur)
oden

5

Au lieu d'éditer chaque modèle, je recommanderais d'écrire une balise de modèle qui renverra tous les champs de n'importe quel modèle donné.
Chaque objet a une liste de champs ._meta.fields.
Chaque objet champ a un attribut namequi renverra son nom et la méthode value_to_string()fournie avec votre modèle objectrenverra sa valeur.
Le reste est aussi simple que cela est dit dans la documentation Django .

Voici mon exemple à quoi pourrait ressembler ce templatetag:

    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

Ouais ce n'est pas joli, tu devras faire ton propre emballage. Jetez un œil à l' application Databrowse intégrée , qui possède toutes les fonctionnalités dont vous avez vraiment besoin.


J'allais dire ... databrowse fait exactement cela, même si j'ai trouvé que c'était une application complètement inutile.
mpen

4

Cela peut être considéré comme un hack, mais je l'ai fait avant d'utiliser modelform_factory pour transformer une instance de modèle en un formulaire.

La classe Form contient beaucoup plus d'informations qui sont très faciles à parcourir et qui serviront le même objectif au détriment d'un peu plus de frais généraux. Si vos ensembles de tailles sont relativement petits, je pense que l'impact sur les performances serait négligeable.

Le seul avantage en plus de la commodité est bien sûr que vous pouvez facilement transformer la table en une grille de données modifiable à une date ultérieure.


4

J'ai mis au point la méthode suivante, qui fonctionne pour moi car dans tous les cas, le modèle aura un ModelForm associé.

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)

Voici un extrait du modèle que j'utilise pour cette vue particulière:

<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>

L'avantage de cette méthode est que je peux choisir modèle par modèle l'ordre dans lequel je souhaite afficher les étiquettes de champ, en utilisant le tuple passé à GetModelData et en spécifiant les noms de champ. Cela me permet également d'exclure certains champs (par exemple, une clé étrangère utilisateur) car seuls les noms de champs passés via le tuple sont intégrés dans le dictionnaire final.

Je ne vais pas accepter cela comme réponse car je suis sûr que quelqu'un peut proposer quelque chose de plus "Djangonic" :-)

Mise à jour: je choisis ceci comme réponse finale car c'est la plus simple de celles données qui fait ce dont j'ai besoin. Merci à tous ceux qui ont apporté des réponses.


3

Solution Django 1.7 pour moi:

Les variables sont exactes à la question, mais vous devriez certainement être en mesure de disséquer cet exemple

La clé ici est d'utiliser à peu près .__dict__le modèle
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)

modèle :

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

dans le modèle, j'ai utilisé un filtre pour accéder au champ dans le 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

J'utilise ceci, 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

Cette approche montre comment utiliser une classe comme ModelForm de django et une balise de modèle comme {{form.as_table}}, mais que toute la table ressemble à une sortie de données, pas à un formulaire.

La première étape a été de sous-classer le widget TextInput de 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))

Ensuite, j'ai sous-classé ModelForm de django pour échanger les widgets par défaut pour les versions en lecture seule:

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" 

Ce sont les seuls widgets dont j'avais besoin. Mais il ne devrait pas être difficile d'étendre cette idée à d'autres widgets.


1

Juste une modification de @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

Laissez Django gérer tous les autres champs autres que les champs associés. Je sens que c'est plus stable



0

Je viens de tester quelque chose comme ça en shell et semble faire son travail:

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

Notez que si vous voulez une représentation str () pour les objets étrangers, vous devez la définir dans leur méthode str . À partir de là, vous avez dict des valeurs pour l'objet. Ensuite, vous pouvez rendre une sorte de modèle ou autre.


0

Django> = 2.0

Ajoutez get_fields()à votre 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]

Puis appelez-le comme object.get_fieldssur votre 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
Bonjour et bienvenue à SO. Veuillez ne pas poster uniquement les réponses. De plus, cette question a déjà une réponse acceptée, le formatage de votre code n'est pas correct, vous utilisez des attributs html obsolètes et le plus important: vous n'expliquez pas comment votre code fournit une meilleure solution que celle acceptée.
Frieder le
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.