¿Cómo puedo guardar la entrada de 3 FormModels desde una vista?
Hola Stackoverflow, esta es mi primera publicación aquí. Acabo de comenzar a aprender a codificar en Python / Django, por lo que mi código puede ser incorrecto, lo siento.
Después de tres días de buscar y probar mucho código, espero que alguien con conocimientos :)
Lo que estoy tratando de hacer:
Un gran formulario: el usuario completa los campos personales, los campos de los expedientes y la información sobre una segunda persona. (parte no se guarda correctamente en este momento)
Views.py una vista para 2 ModelForms y reutilizando una nuevamente para c_person. Models.py 2 modelos (un dosier y un modelo de datos de una persona). Forms.py 2 ModelForms (uno de dossier y otro de persona, reutilizando el formulario de persona dentro de la vista).
Los datos dossier.html para dar una idea del formulario (se eliminó una gran cantidad de código).
<div class="column is-one-fifth">
<label class="label is-small">Firstname</label>
{{ person_form.first_name }}
</div>
<div class="column is-one-fifth">
<label class="label is-small">Preposition</label>
{{ person_form.preposition }}
</div>
<div class="column is-one-fifth">
<label class="label is-small">Lastname</label>
{{ person_form.last_name }}
</div>
<div class="column is-one-fifth">
<label class="label is-small">Address</label>
{{ person_form.address }}
</div>
<div class="column is-one-fifth">
<label class="label is-small">Firstname</label>
{{ cperson_form.first_name }}
</div>
<div class="column is-one-fifth">
<label class="label is-small">Lastname</label>
{{ cperson_form.last_name }}
</div>
<div class="column is-one-fifth">
<label class="label is-small">Number</label>
{{ cperson_form.number }}
</div>
<div class="column is-one-fifth">
<label class="label is-small">Status</label>
{{ dossier_form.status }}
</div>
<div class="column is-one-fifth">
<label class="label is-small">Operator</label>
<div class=select>{{ dossier_form.operator }}</div>
</div>
models.py
class Person(models.Model):
number = models.IntegerField(unique=True, null=True, blank=True)
first_name = models.CharField(max_length=35, null=True, blank=True)
last_name = models.CharField(max_length=35, null=True, blank=True)
initials = models.CharField(max_length=10, null=True, blank=True)
preposition = models.CharField(max_length=10, null=True, blank=True)
address = models.CharField(max_length=80, null=True, blank=True)
address_number = models.CharField(max_length=5, null=True, blank=True)
email = models.EmailField(null=True, blank=True)
phone_number = models.IntegerField(null=True, blank=True)
@property
def full_name(self):
"Returns the person's full name"
full_name = (self.last_name + ', ' + self.first_name)
# Need to check this, django return 'None' if no preposition is used
if self.preposition:
full_name = full_name + ' ' + self.preposition
return full_name
def __str__(self):
return self.first_name + ' ' + self.last_name
class Dossier(models.Model):
# Core
status = models.ForeignKey('DossierStatus', null=True, on_delete=models.SET_NULL)
operator = models.ForeignKey('User', null=True, on_delete=models.SET_NULL)
d_person = models.ForeignKey('Person', related_name='d_person', null=True, on_delete=models.SET_NULL, blank=True)
c_person = models.ForeignKey(Person, related_name='c_person', null=True, on_delete=models.SET_NULL, blank=True)
def __int__(self):
return self.id
def get_absolute_url(self):
return reverse('dossier_edit', kwargs={'pk': self.pk})
Forms.py
class PersForm(forms.ModelForm):
class Meta:
model = Person
fields = ['number', 'first_name', 'preposition', 'last_name', 'initials', 'address', 'address_number', 'email', 'phone_number']
class DossierForm(forms.ModelForm):
class Meta:
model = Dossier
fields = '__all__'
Views.py
def dossier(request):
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
dossier_form = DossierForm(request.POST, request.FILES)
dossier_form_valid = dossier_form.is_valid()
person_form = PersForm(request.POST)
person_form_valid = person_form.is_valid()
cperson_form = PersForm(request.POST)
cperson_form_valid = cperson_form.is_valid()
# check whether it's valid:
if dossier_form_valid and person_form_valid and cperson_form_valid:
print("succes")
# process the data
person_save = person_form.save()
dossier_save = dossier_form.save(commit=False)
cperson_save = cperson_form.save(commit=False)
dossier_save.person_save = person_save
cperson_save.dossier_save = dossier_save
cperson_save.save()
# redirect to a new URL:
return HttpResponseRedirect('/data/index/')
else:
print("failure")
print(dossier_form.errors)
print(person_form.errors)
# if a GET (or any other method) we'll create a blank form
else:
dossier_form = DossierForm()
person_form = PersForm()
cperson_form = PersForm()
return render(request, 'dossier.html',
{'title': 'Nieuwe aanvraag', 'dossier_form': dossier_form,
'person_form': person_form, 'cperson_form': cperson_form})
El problema aquí es que solo se guardan los datos del formulario de una persona en la base de datos. Probé muchas variaciones diferentes en este código:
person_save = person_form.save()
dossier_save = dossier_form.save(commit=False)
cperson_save = cperson_form.save(commit=False)
dossier_save.person_save = person_save
cperson_save.dossier_save = dossier_save
cperson_save.save()
2 respuestas
Al pasar el argumento commit
al método save()
en un formulario de modelo, guardará su modelo en la base de datos. Veo que el término commit
aquí es un poco engañoso, porque suena como confirmar la transacción de la base de datos , pero simplemente define si se llamará o no al método save()
del modelo. .
Lo que quiere hacer es envolver todo este código en una sola transacción de base de datos, usando django.models.db.transaction
.
Primero impórtelo en su vista:
from django.db.models import transaction
Entonces puedes usarlo así:
if dossier_form_valid and person_form_valid and cperson_form_valid:
print("succes")
# process the data
with transaction.atomic():
d_person = person_form.save()
c_person = cperson_form.save()
dossier = dossier_form.save(commit=False)
dossier.d_person = d_person
dossier.c_person = c_person
dossier.save()
El método model form save()
devuelve una instancia del modelo Django que representa. Así que lo renombró solo a dossier
en lugar de dossier_save
, etc.
En dossier_form
puede usar commit=False
porque tiene más datos para asociar con dossier
, de esa manera evita llamar al método save()
varias veces. Pero para que el objeto persista en la base de datos, después de agregar los datos adicionales, debe llamar al método de guardar como dossier.save()
.
Todo dentro del bloque transaction.atomic()
se ejecutará en una sola transacción de la base de datos, si ocurre un error, Django se revertirá automáticamente.
La respuesta de Vitor es correcta, sin embargo, la importación ha cambiado de:
from django.db.models import transaction
Ha cambiado a:
from django.db import transaction
Nuevas preguntas
django
Django es un marco de aplicación web del lado del servidor de código abierto escrito en Python. Está diseñado para reducir el esfuerzo requerido para crear sitios web y aplicaciones web complejos basados en datos, con un enfoque especial en menos código, sin redundancia y siendo más explícito que implícito.