web-dev-qa-db-fra.com

Création d'un modèle et de modèles associés avec des jeux de formulaires en ligne

[J'ai également posté ceci sur tilisateurs Django | Groupes Google .]

En utilisant l'exemple dans les documents inset formset form , je peux éditer objets appartenant un modèle particulier (à l'aide de modèles). J'ai essayé de suivre le même modèle pour création de nouveaux objets à l'aide de jeux de formulaires en ligne, mais je n'ai pas pu effacer ma tête assez pour faire ressortir une vision de travail à cet effet.

En utilisant le même exemple que dans le lien ci-dessus, comment pourrais-je créer une nouvelle instance d'un modèle "Auteur" avec ses objets "Livre" associés?

43
chefsmart

Créez d'abord un formulaire de modèle d'auteur.

author_form = AuthorModelForm()

puis créez un objet auteur factice:

author = Author()

Ensuite, créez un jeu de formulaires en ligne en utilisant l'auteur factice comme ceci:

formset = BookFormSet(instance=author)  #since author is empty, this formset will just be empty forms

Envoyez cela à un modèle. Une fois les données retournées à la vue, vous créez l'auteur:

author = AuthorModelForm(request.POST)
created_author = author.save()  # in practice make sure it's valid first

Maintenant, connectez le jeu de formulaires en ligne avec l'auteur nouvellement créé, puis enregistrez:

formset = BookFormSet(request.POST, instance=created_author)
formset.save()   #again, make sure it's valid first

éditer:

Pour ne pas avoir de cases à cocher sur les nouveaux formulaires, procédez comme suit:

{% for form in formset.forms %}
    <table>
    {% for field in form %}
        <tr><th>{{field.label_tag}}</th><td>{{field}}{{field.errors}}</td></tr>
    {% endfor %}

    {% if form.pk %} {# empty forms do not have a pk #}
         <tr><th>Delete?</th><td>{{field.DELETE}}</td></tr>
    {% endif %}
    </table>
{% endfor %}
38
priestc

J'aimerais en fait proposer un petit ajustement à la solution de nbv4:

Supposons que vous ne créez pas le created_author vide en dehors de l'instruction if-else et que vous devez donc imbriquer le formset à l'intérieur de author_form.is_valid () pour éviter les erreurs d'exécution lorsque le author_form n'est pas valide (et donc aucun created_author n'est instancié).

Au lieu de:

if request.method == 'POST':
    author_form = AuthorModelForm(request.POST)
    if author_form.is_valid():
        created_author = author_form.save()
        formset = BookFormSet(request.POST, instance=created_author)
        if formset.is_valid():
            formset.save()
            return HttpResponseRedirect(...)
else:
    ...

Procédez comme suit:

if request.method == 'POST':
    author_form = AuthorModelForm(request.POST)
    if author_form.is_valid():
        created_author = author_form.save(commit=False)
        formset = BookFormSet(request.POST, instance=created_author)
        if formset.is_valid():
            created_author.save()
            formset.save()
            return HttpResponseRedirect(...)
else:
    ...

Cette version évite de valider le created_author jusqu'à ce que le book_formset ait eu une chance de valider. Le cas d'utilisation à corriger est que quelqu'un remplit un AuthorForm valide avec un BookFormSet non valide et continue de soumettre à nouveau, créant plusieurs enregistrements Author sans aucun livre associé. Cela semble fonctionner pour mon application de suivi de projet (remplacer "Auteur" par "Projet" et "Livre" par "Rôle").

37
MikeRand

models.py (Contact)

class Contact(models.Model)
    first = models.CharField(max_length=30)
    middle = models.CharField('M.I.',max_length=30, blank=True)
    last = models.CharField(max_length=30)
    sort_order = models.PositiveIntegerField(default=99)

models.py (Lien)

class Link(models.Model):
    contact = models.ForeignKey(Contact)
    link = models.URLField()
    description = models.CharField(max_length=30)
    access_date = models.DateField(blank=True,null=True)

forms.py

from Django.forms import ModelForm
from contacts.models import Contact

class ContactAjaxForm(ModelForm):
    class Meta:
        model=Contact

views.py

def edit(request,object_id=False):
    LinkFormSet = inlineformset_factory(Contact, Link, extra=1)
    if object_id:
        contact=Contact.objects.get(pk=object_id)
    else:
        contact=Contact()
    if request.method == 'POST':
        f=forms.ContactAjaxForm(request.POST, request.FILES, instance=contact)
        fs = LinkFormSet(request.POST,instance=contact)
        if fs.is_valid() and f.is_valid():
            f.save()
            fs.save()
            return HttpResponse('success')
    else:
        f  = forms.ContactAjaxForm(instance=contact)
        fs = LinkFormSet(instance=contact)
    return render_to_response(
        'contacts/edit.html', 
        {'fs': fs, 'f': f, 'contact': contact}
    )

Ce n'est pas basé sur l'exemple du livre, il est édité à partir d'un code sur mon site. Je ne l'ai pas testé donc il pourrait y avoir des bugs mais globalement ça devrait être solide. L'utilisation d'une instance vide de Contact n'est pas la méthode suggérée, mais elle économise un peu de logique et cela fonctionne.

Edit: Ajouté le modèle de lien, passé à la clé étrangère normale au lieu de la clé étrangère générique, ce qui prête à confusion

9
Ogre Codes