2012-12-27 1 views
0

J'ai vraiment tiré mes cheveux pour faire cela. J'ai 2 champs entiers dans un de mes modèles comme suit:est-il possible pour un multi-widget d'utiliser deux champs de modèle différents

#models.py 

class TestModel(models.Model): 
    """ 
    This is a test model. 
    """ 

    max = models.IntegerField() 
    min = models.IntegerField() 

Comme les deux valeurs ci-dessus sont je les voulais liés entre eux d'utiliser un widget personnalisé. Maintenant, voici le point, je veux que ces champs utilisent un seul widget personnalisé (MultiWidget) mais je veux aussi garder les valeurs dans des colonnes séparées dans la base de données [Ne pas vraiment vouloir analyser les choses et de plus la fonctionnalité de recherche utilisera le ci-dessus les champs séparément, donc je veux juste les stocker séparément]. Un widget personnalisé n'accepterait que la valeur d'un seul champ de modèle, j'ai donc décidé de créer un champ de formulaire personnalisé à utiliser dans le ModelForm pour le modèle ci-dessus.

Voici à quoi ressemble mon formulaire.

class MinMaxField(forms.MultiValueField): 

    def __init__(self, *args, **kwargs): 

     all_fields = ( 
      forms.CharField(), 
      forms.CharField(), 
      ) 

     super(MinMaxField, self).__init__(all_fields, *args, **kwargs) 

    def compress(self, values): 
     if values: 
      return '|'.join(values) 
     return '' 

class TestModelForm(forms.ModelForm): 

    minmax = MinMaxField(widget=widgets.MinMaxWidget(), 
      required=False) 

    class Meta: 
     model = models.TestModel 
     fields = ('min', 'max', 'minmax') 
     widgets = { 
      'min' : forms.HiddenInput(), 
      'max' : forms.HiddenInput(), 
     } 

    def full_clean(self, *args, **kwargs): 
     if 'minmax_0' in self.data: 
      newdata = self.data.copy() 
      newdata['min'] = self.data['minmax_0'] 
      newdata['max'] = self.data['minmax_1'] 
      self.data = newdata 
     super(TestModelForm, self).full_clean(*args, **kwargs) 

Le formulaire ci-dessus se cache essentiellement les champs du modèle et ne montre que le champ minmax qui utilise un widget personnalisé comme suit:

class MinMaxWidget(widgets.MultiWidget): 

    def __init__(self, attrs=None): 
     mwidgets = (widgets.Select(attrs=attrs, choices=MIN), 
        widgets.Select(attrs=attrs, choices=MAX)) 
     super(MinMaxWidget, self).__init__(mwidgets, attrs) 

    def value_from_datadict(self, data, files, name): 
     try: 
      values = [widget.value_from_datadict(data, files, name + '_%s' % i)\ 
         for i, widget in enumerate(self.widgets)] 

      value = [int(values[0]), int(values[1])] 
     except ValueError: 
      raise ValueError('Value %s for %s did not validate. \ 
           a list or a tuple expected' % (value, self.widget)) 
     return value 

    def decompress(self, value): 
     if value: 
      if isinstance(value, list): 
       return value 
      return [value[0], value[1]] 
     return [None, None] 

    def format_output(self, rendered_widgets): 
     rendered_widgets.insert(0, '<span class="multi_selects">') 
     rendered_widgets.insert(-1, '<label id="min">Min</label>') 
     rendered_widgets.append('<label id="max">Max</label></span>') 
     return u''.join(rendered_widgets) 

maintenant à ce tout moment fonctionne très bien, les valeurs sont sauvés séparément dans leurs champs respectifs. Mais le problème se pose lorsque j'essaie d'éditer le formulaire avec une instance d'un objet TestModel. Dans le cas de l'édition, lorsque le formulaire est rendu, les valeurs réelles sont stockées dans les champs de saisie cachés min et max et le champ minmax n'a pas de valeur. [Ofcourse comme il est un champ de formulaire.]

Quelles sont les options que je dois

  1. Soit préremplir la valeur du champ de formulaire minmax des min et max champs. [Remarque: sans utiliser JavaScript ou jQuery]

  2. Ou de créer un MultiWidget qui utilise les valeurs de deux champs de modèle différents.

Toute aide serait appréciée. Merci.

+0

quelle version utilisez-vous? –

+0

Quelqu'un at-il trouvé un moyen de le faire ... même problème ici ... – gabn88

Répondre

0

Je suis actuellement confronté à un problème similaire et il existe un moyen d'utiliser un widget pour deux champs. J'utilise la version 1.4 et je n'en ai pas essayé d'autre.

À votre avis, vous avez probablement quelque chose comme ceci:

form = TestModelForm(instance=instance) 

instance est l'objet de votre base de données que vous souhaitez modifier. Voyons voir ce que Django fait quand il crée votre formulaire

# django/forms/models.py 

class BaseModelForm(BaseForm): 
    def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, 
        initial=None, error_class=ErrorList, label_suffix=':', 
        empty_permitted=False, instance=None): 
      ... 
      if instance is None: 
       ... 
      else: 
       self.instance = instance 
       object_data = model_to_dict(instance, opts.fields, opts.exclude) 
      # if initial was provided, it should override the values from instance 
      if initial is not None: 
       object_data.update(initial) 
      ... 
      super(BaseModelForm, self).__init__(data, files, auto_id, prefix, 
               object_data, error_class, 
               label_suffix, empty_permitted) 

puis, lorsque le formulaire est rendu, BoundField classe utilise cette object_data pour attribuer une valeur initiale au widget.

Par conséquent, tout ce que vous devez faire est de fournir initial dans votre constructeur de formulaire.

class TestModelForm(forms.ModelForm): 
    def __init__(self, *args, **kwargs): 
     if 'instance' in kwargs: 
      initial = dictionary with initial values 
      initial.update(kwargs['initial'] or {}) 
     ... 
     super(TestModelForm, self).__init__(initial=initial, *args, **kwargs) 

Rappelez-vous aussi ajouter votre champ à vos formulaires fields liste. De cette façon, vous n'avez pas deux widgets cachés inutilisés, pas besoin d'utiliser javascript et imo, le code est plus explicatif.

Questions connexes