Domanda

Ho il seguente:

class AccountAdmin(models.Model):

    account = models.ForeignKey(Account)
    is_master = models.BooleanField()
    name = models.CharField(max_length=255)
    email = models.EmailField()

    class Meta:
        unique_together = (('Account', 'is_master'), ('Account', 'username'),)

Se poi creare un nuovo AccountAdmin con lo stesso nome utente di un altro sullo stesso account, invece di esso mi dà un errore di visualizzazione del modello, si rompe con un IntegrityError e la pagina muore.Mi auguro che a mio modo di vedere, ho potuto solo andare:

if new_accountadmin_form.is_valid():
    new_accountadmin_form.save()

Come faccio a conquistare questo problema.C'è un secondo is_valid() tipo di metodo che controlla il DB per la violazione delle unique_together = (('Account', 'is_master'), ('Account', 'username'),) parte?

Non vorrei prendere un IntegrityError a mio modo di vedere.Che la logica di dominio mescolato con la logica di presentazione.Viola ASCIUTTO perché, se devo visualizzare la stessa forma su 2 pagine, dovrò ripetere lo stesso blocco.Inoltre, viola ASCIUTTO perché se sono due forme della stessa cosa, devo scrivere la stessa, ad eccezione di:di nuovo.

È stato utile?

Soluzione

Ci sono due opzioni:

a) Sono in un blocco try in cui si salva il modello e catturare l'IntegrityError e trattare con esso.Qualcosa di simile a:

try:
    new_accountadmin_form.save()
except IntegrityError:
    new_accountadmin_form._errors["account"] = ["some message"]
    new_accountadmin_form._errors["is_master"] = ["some message"]

    del new_accountadmin_form.cleaned_data["account"]
    del new_accountadmin_form.cleaned_data["is_master"]

b) il metodo clean() del modulo, controllare se la fila esiste e sollevare un forms.ValidationError con un messaggio appropriato.Esempio qui.


Così, b) è...Che è il motivo per cui ho di riferimento documentazione;tutto ciò che serve è lì.

Ma sarebbe qualcosa di simile:

class YouForm(forms.Form):
    # Everything as before.
    ...

    def clean(self):
       """ This is the form's clean method, not a particular field's clean method """
       cleaned_data = self.cleaned_data

       account = cleaned_data.get("account")
       is_master = cleaned_data.get("is_master")
       username = cleaned_data.get("username")

       if AccountAdmin.objects.filter(account=account, is_master=is_master).count() > 0:
           del cleaned_data["account"]
           del cleaned_data["is_master"]
           raise forms.ValidationError("Account and is_master combination already exists.")

       if AccountAdmin.objects.filter(account=account, username=username).count() > 0:
           del cleaned_data["account"]
           del cleaned_data["username"]
           raise forms.ValidationError("Account and username combination already exists.")

    # Always return the full collection of cleaned data.
    return cleaned_data

Per quello che vale - mi sono appena reso conto che il tuo unique_together di cui sopra si fa riferimento a un campo chiamato username che non è rappresentato nel modello.

Il pulito metodo di cui sopra è chiamato dopo tutto pulito metodi per i singoli campi sono chiamati.

Altri suggerimenti

E per un completamente modo generico.Nel modello sono le seguenti due helper fns:

def getField(self,fieldName):
  # return the actual field (not the db representation of the field)
  try:
    return self._meta.get_field_by_name(fieldName)[0]
  except models.fields.FieldDoesNotExist:
    return None

e

def getUniqueTogether(self):
  # returns the set of fields (their names) that must be unique_together
  # otherwise returns None
  unique_together = self._meta.unique_together
  for field_set in unique_together:
    return field_set
  return None

E in forma sono i seguenti fn:

def clean(self):
  cleaned_data = self.cleaned_data
  instance = self.instance

  # work out which fields are unique_together
  unique_filter = {}
  unique_fields = instance.getUniqueTogether()
  if unique_fields:
    for unique_field in unique_fields:
      field = instance.getField(unique_field)
      if field.editable: 
        # this field shows up in the form,
        # so get the value from the form
        unique_filter[unique_field] = cleaned_data[unique_field]
      else: 
        # this field is excluded from the form,
        # so get the value from the model
        unique_filter[unique_field] = getattr(instance,unique_field)

    # try to find if any models already exist in the db;
    # I find all models and then exlude those matching the current model.
    existing_instances = type(instance).objects.filter(**unique_filter).exclude(pk=instance.pk)

    if existing_instances:
      # if we've gotten to this point, 
      # then there is a pre-existing model matching the unique filter
      # so record the relevant errors
      for unique_field in unique_fields:
        self.errors[unique_field] = "This value must be unique."

Modello.Meta.unique_together crea un vincolo limitato al database, mentre ModelForm.is_valid() si basa principalmente sul tipo corretto.Evento che, se fosse vincoli check si avrebbe una condizione di competizione che potrebbe ancora causare un IntegrityError nella chiamata di save ().

Probabilmente si vorrà essere la cattura di IntegrityError:

if new_accountadmin_form.is_valid():
    try:
        newaccountadmin_form.save()
    except IntegrityError, error:
        # here's your error handling code
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top