2010-08-04 5 views
6

Je sais que si j'ai besoin d'un "sélecteur" personnalisé pour un champ dans django-admin, j'ai besoin de créer un widget personnalisé. Mais que faire si le widget doit produire deux valeurs, par exemple les coordonnées X et Y, comment puis-je les remplir dans deux champs différents du modèle?Valeurs de remplissage de widget dans deux champs

+0

Pouvez-vous expliquer votre question dans details.Is il une relation entre x et y – ha22109

+0

X et Y sont coordonnées prises à partir d'une carte et je veux les stocker dans des champs distincts de le modèle. Existe-t-il un moyen d'atteindre cet objectif avec le widget, ou dois-je modifier la fonction de sauvegarde du modèle? –

+0

Questions similaires: http://stackoverflow.com/questions/2856790/ http://stackoverflow.com/questions/9333406/ – Jelko

Répondre

3

Jannis Leidel a sorti un widget il y a très longtemps. Autant que je me souvienne, il a pris les coordonnées d'une carte et lui a passé un seul champ et un certain javascript l'a coupé en 2 coordonnées pour 2 champs.

Combiné avec une coutume form il devrait fonctionner assez bien

+2

@downvoter: veuillez expliquer pourquoi vous avez trouvé cette réponse down-vote digne – vikingosegundo

7

Vous pouvez regarder la mise en œuvre du champ date-heure, qui rend comme 2 champs dans l'admin.

Aller haut vers le bas,

l'administrateur utilise

class AdminSplitDateTime(forms.SplitDateTimeWidget): 
    """ 
    A SplitDateTime Widget that has some admin-specific styling. 
    """ 
    def __init__(self, attrs=None): 
     widgets = [AdminDateWidget, AdminTimeWidget] 
     # Note that we're calling MultiWidget, not SplitDateTimeWidget, because 
     # we want to define widgets. 
     forms.MultiWidget.__init__(self, widgets, attrs) 

    def format_output(self, rendered_widgets): 
     return mark_safe(u'<p class="datetime">%s %s<br />%s %s</p>' % \ 
      (_('Date:'), rendered_widgets[0], _('Time:'), rendered_widgets[1])) 

qui à son tour utilise SplitDateTimeWidget:

class SplitDateTimeWidget(MultiWidget): 
    """ 
    A Widget that splits datetime input into two <input type="text"> boxes. 
    """ 
    date_format = DateInput.format 
    time_format = TimeInput.format 

    def __init__(self, attrs=None, date_format=None, time_format=None): 
     if date_format: 
      self.date_format = date_format 
     if time_format: 
      self.time_format = time_format 
     widgets = (DateInput(attrs=attrs, format=self.date_format), 
        TimeInput(attrs=attrs, format=self.time_format)) 
     super(SplitDateTimeWidget, self).__init__(widgets, attrs) 

    def decompress(self, value): 
     if value: 
      return [value.date(), value.time().replace(microsecond=0)] 
     return [None, None] 

Ce qui, à son tour prolonge la MultiWidget définie dans django.forms.widgets que vous devez également étendre . Il a beaucoup de méthodes utiles que vous pouvez remplacer.

class MultiWidget(Widget): 
""" 
A widget that is composed of multiple widgets. 

Its render() method is different than other widgets', because it has to 
figure out how to split a single value for display in multiple widgets. 
The ``value`` argument can be one of two things: 

    * A list. 
    * A normal value (e.g., a string) that has been "compressed" from 
     a list of values. 

In the second case -- i.e., if the value is NOT a list -- render() will 
first "decompress" the value into a list before rendering it. It does so by 
calling the decompress() method, which MultiWidget subclasses must 
implement. This method takes a single "compressed" value and returns a 
list. 

When render() does its HTML rendering, each value in the list is rendered 
with the corresponding widget -- the first value is rendered in the first 
widget, the second value is rendered in the second widget, etc. 

Subclasses may implement format_output(), which takes the list of rendered 
widgets and returns a string of HTML that formats them any way you'd like. 

You'll probably want to use this class with MultiValueField. 
""" 
def __init__(self, widgets, attrs=None): 
    self.widgets = [isinstance(w, type) and w() or w for w in widgets] 
    super(MultiWidget, self).__init__(attrs) 

def render(self, name, value, attrs=None): 
    # value is a list of values, each corresponding to a widget 
    # in self.widgets. 
    if not isinstance(value, list): 
     value = self.decompress(value) 
    output = [] 
    final_attrs = self.build_attrs(attrs) 
    id_ = final_attrs.get('id', None) 
    for i, widget in enumerate(self.widgets): 
     try: 
      widget_value = value[i] 
     except IndexError: 
      widget_value = None 
     if id_: 
      final_attrs = dict(final_attrs, id='%s_%s' % (id_, i)) 
     output.append(widget.render(name + '_%s' % i, widget_value, final_attrs)) 
    return mark_safe(self.format_output(output)) 

def id_for_label(self, id_): 
    # See the comment for RadioSelect.id_for_label() 
    if id_: 
     id_ += '_0' 
    return id_ 
id_for_label = classmethod(id_for_label) 

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

def _has_changed(self, initial, data): 
    if initial is None: 
     initial = [u'' for x in range(0, len(data))] 
    else: 
     if not isinstance(initial, list): 
      initial = self.decompress(initial) 
    for widget, initial, data in zip(self.widgets, initial, data): 
     if widget._has_changed(initial, data): 
      return True 
    return False 

def format_output(self, rendered_widgets): 
    """ 
    Given a list of rendered widgets (as strings), returns a Unicode string 
    representing the HTML for the whole lot. 

    This hook allows you to format the HTML design of the widgets, if 
    needed. 
    """ 
    return u''.join(rendered_widgets) 

def decompress(self, value): 
    """ 
    Returns a list of decompressed values for the given compressed value. 
    The given value can be assumed to be valid, but not necessarily 
    non-empty. 
    """ 
    raise NotImplementedError('Subclasses must implement this method.') 

def _get_media(self): 
    "Media for a multiwidget is the combination of all media of the subwidgets" 
    media = Media() 
    for w in self.widgets: 
     media = media + w.media 
    return media 
media = property(_get_media) 

def __deepcopy__(self, memo): 
    obj = super(MultiWidget, self).__deepcopy__(memo) 
    obj.widgets = copy.deepcopy(self.widgets) 
    return obj 
+0

Je ne suis pas fini de construire mon widget mais à partir des progrès actuels, je pense que c'est la solution que je cherchais. Il est étrange que je n'ai pas pu trouver d'informations sur les multi-widgets dans la documentation officielle de Django, donc si vous en connaissez, veuillez nous les fournir. –

+0

J'ai trouvé que multi-widget est juste un moyen de gérer plusieurs widgets de différents, puis lier les données ensemble dans un seul champ. Donc, il semble maintenant plus comme le JS wasy que vikingosegundo offert est le bon –

+4

résout le problème inverse à la question posée, c'est comment faire champ unique -> deux widgets – Anentropic

0

Vous pouvez faire le widget rendu deux (cachés) entrées html, dont les noms se rapportent aux champs du modèle qui doivent être remplies et affecter les valeurs nécessaires via javascript pour eux!

+0

Est-ce que cela fonctionne si elles ne font pas partie du formulaire? –

Questions connexes