2010-02-19 22 views
22

Estoy usando Django ModelForms para crear un formulario. Tengo mi formulario configurado y funciona bien.Django Formas con get_or_create

form = MyForm(data=request.POST) 

if form.is_valid(): 
    form.save() 

Lo que ahora quiero sin embargo es que el formulario verifique primero si existe un registro idéntico. Si lo hace, quiero que obtenga el id de ese objeto y, si no, quiero que lo inserte en la base de datos y luego me proporcione la identificación de ese objeto. ¿Es esto posible usar algo como:

form.get_or_create(data=request.POST) 

sé que podría hacer

form = MyForm(instance=object) 

al crear la forma, pero esto no funcionaría, ya que todavía quiere tener el caso en que no hay una instancia de una objetar

edición:

decir que mi modelo es

class Book(models.Model): 
    name = models.CharField(max_length=50) 
    author = models.CharField(max_length=50) 
    price = models.CharField(max_length=50) 

Quiero un formulario que alguien pueda completar para almacenar libros. Sin embargo, si ya hay un libro en el db que tiene el mismo nombre, autor y precio, obviamente no quiero que este registro se vuelva a agregar, así que solo quiero encontrar su identificación y no agregarla.

Sé que hay una función en Django; get_or_create que hace esto, pero ¿hay algo similar para los formularios? o tendría que hacer algo como

if form.is_valid(): 
    f = form.save(commit=false) 
    id = get_or_create(name=f.name, author=f.author, price=f.price) 

Gracias

Respuesta

27

me gusta este enfoque:

if request.method == 'POST': 
    form = MyForm(request.POST) 
    if form.is_valid(): 
     book, created = Book.objects.get_or_create(**form.cleaned_data) 

De esta manera se llega a tomar ventaja de todas las funcionalidades de los modelos de formularios (excepto .save()) y el acceso directo get_or_create.

+1

Parece un buen enfoque, pero realmente no sigues el '** form.cleaned_data' bit. Específicamente, ¿para qué son **? ¿Alguien puede elaborar? –

+5

el '**' descomprime el diccionario. ver: http://docs.python.org/tutorial/controlflow.html#unpacking-argument-lists –

+0

@rz: Gracias por la aclaración. –

1

Sólo tiene dos casos en la vista antes de la devolución de datos se ha producido, algo así como

if id: 
    form = MyForm(instance=obj) 
else 
    form = MyForm() 

entonces se puede llamar form.save() en la devolución de datos y Django se encargará del resto.

+0

Con el supuesto de que después de que el "si id" usted crea una instancia de la variable obj correctamente. – Tom

+0

ver la edición anterior para más información. Gracias – John

1

¿Qué quiere decir con "si existe un registro idéntico"? Si se trata de un simple control de identidad, entonces su código de la vista sería algo como esto:

if request.method == 'POST': 
    form = MyForm(request.POST) 
    if form.is_valid(): 
     form.save() 
else: 
    if get_id: 
     obj = MyModel.objects.get(id=get_id) 
     form = MyForm(instance=obj) 
    else: 
     form = MyForm() 

El concepto aquí es el cheque se produce en la solicitud GET, de tal manera que en el POST para guardar, Django ya han determinado si este es un registro nuevo o existente.

Si su comprobación de un registro idéntico es más compleja, podría requerir un cambio de lógica un poco.

+0

ver la edición anterior para más comentarios. Gracias – John

0

Me gustaría hacer esto -

if request.method == 'POST': 
    form = MyForm(request.POST) 
    if form.is_valid(): 
     name = form.cleaned_data['name'] 
     author = form.cleaned_data['author'] 
     price = form.cleaned_data['prince'] 

     if name and author and price: 
      book, created = Book.objects.get_or_create(name=name, \ 
       author=author, price=price) 

      if created: 
       # fresh entry in db. 
      else: 
       # already there, maybe update? 

      book.save() 
0

Sobre la base de las respuestas y comentarios, tuve que crear una solución diferente para mi caso, que incluía el uso de unique_together en el modelo base. También puede encontrar este código útil, ya que en realidad lo hice bastante genérico.

Tengo un código personalizado en el método form.save() que quiero utilizar para crear un objeto nuevo, por lo que no quiero simplemente no utilizar la llamada form.save(). Tengo que poner mi código de verificación en el método form.save(), que creo que es un lugar razonable para ponerlo.

Tengo una función de utilidad para aplanar los iterables.

def flatten(l, a=list()): 
    """ 
     Flattens a list. Just do flatten(l). 
     Disregard the a since it is used in recursive calls. 
    """ 
     for i in l: 
      if isinstance(i, Iterable): 
       flatten_layout(i, a) 
      else: 
       a.append(i) 
     return a 

En el ModelForm, que sobrescribir el validate_unique() método:

def validate_unique(self): 
    pass 

Esto es lo que mi método Guardar parece:

def save(self, commit=True): 
    unique_fields = flatten(MyObject._meta.unique_together) 
    unique_cleaned_data = {k: v for k, v in self.cleaned_data.items() if k in unique_fields} 
    # check if the object exists in the database based on unique data 
    try: 
     my_object = MyObject.objects.get(**unique_cleaned_data) 
    except MyObject.DoesNotExist: 
     my_object = super(MyModelFormAjax, self).save(commit) 
     # -- insert extra code for saving a new object here --- 
    else: 
     for data, value in self.cleaned_data.items(): 
      if data not in unique_fields: 
       # only update the field if it has data; otherwise, retain 
       # the old value; you may want to comment or remove this 
       # next line 
       if value: 
        setattr(my_object, data, value) 

     if commit: 
      my_object.save() 
    return my_object 
Cuestiones relacionadas