2011-10-24 3 views
1

J'ai un modèle avec un ForeignKey à un autre modèle qui a aussi un ForeignKey. Je le rends avec un assistant de formulaire django (en essayant de supporter une version aussi ancienne que possible de django) qui n'est pas inlineformset friendly. Je veux que l'utilisateur mette à la fois les modèles et les informations supplémentaires dans l'assistant de formulaire et traduise cela en la valeur pk correcte (de cette façon, les informations supplémentaires peuvent dépendre d'une combinaison) Je m'interroge sur la meilleure façon d'aborder cela.Champ ForeignKey avec un autre ForeignKey dans FormWizard avec MultiValueField

Pour clarifier davantage avec le code. J'ai trois modèles:

class Subject(models.Model): 
    title = models.CharField(...) 
    extra_info = models.CharField(...) 

class Topic(models.Model): 
    title = models.CharField(...) 
    extra_info = models.CharField(...) 
    subject = models.ForeignKey(Subject) 

class AwesomeThing(models.Model): 
    title = models.CharField(...) 
    topic = models.ForeignKey(Topic) 

Maintenant, je veux présenter le sujet domaine de AwesomThing à l'utilisateur dans mon assistant de formulaire comme quatre champs:

  • Sujet
  • Sujet supplémentaire information
  • Sujet
  • Sujet information supplémentaire

J'utilise un MultiValueField avec MultiWidget pour accomplir cela, mais je ne suis pas sûr de la meilleure façon de garder la valeur et le transférer entre les étapes de l'assistant de formulaire. Je suis capable de le faire mais j'ai peur que ma méthode frappe trop fréquemment la base de données. Voici comment je fais actuellement ceci:

class SubjectTopicField(MultiValueField): 
    widget = SubjectTopicInput # Multiwidget to present four input fields 
    hidden_widget = HiddenInput 

    def __init__(self, *args, **kwargs): 
    fields = (
     CharField(label='Subject'), 
     CharField(label='Subject extra information'), 
     CharField(label='Topics'), 
     CharField(label='Topic extra information'), 
     ) 

    super(SubjectTopicField, self).__init__(fields, *args, **kwargs) 

    def compress(self, data_list): 
    # If all four fields are present ... 
    if data_list and len(data_list) == 4: 
     # ... call and return the topic 'pk' value from a custom method that 
     # creates and/or gets the topic based on the subject and topic info 
     return get_or_create_topic(data_list[0], data_list[1], # Subject 
           data_list[2], data_list[3]) # Topic 

    return None 

Ma méthode get_or_create_topic ressemble fondamentalement juste au si cette combinaison de sujet et le sujet existe et si oui retourne et sinon crée. Le problème est que cela signifie qu'il doit frapper la base de données à chaque étape. Je vois que la présentation du champ caché de ce champ ne contient que la liste des valeurs (la data_list) au lieu de la valeur pk du sujet. Ce n'est pas optimal à mon avis. Y a-t-il une meilleure manière de faire cela? Je pourrais juste être concentré sur obtenir ceci pour fonctionner avec MultiValueField que je ne vois pas la bonne manière de faire ceci.

+0

J'ai compris cela. Comme je ne peux pas répondre moi-même à mon message en raison de sa réputation, je vais attendre quelques heures avant de poster la réponse à ma question. –

Répondre

1

Je l'ai compris, je pense. La réponse a été d'utiliser une sous-classe MultiWidget mais de ne pas sous-classer un MultiValueField. Je pointe juste mon domaine sujets à mon SubjectTopicInput qui hérite MultiWidget puis en plus de décompresser je sous-classe value_from_datadict pour retourner le pk (je sais que je peux le rendre plus joli mais c'est juste ce que j'ai fait pour le faire fonctionner):

def value_from_datadict(self, data, files, name): 
    # Is there just a single value available which we can return? 
    single_value = data.get(name, None) 
    if single_value: return single_value 

    # No single value, let's try to find our topic (or create it) 
    super_value = super(SubjectTopicInput, self).value_from_datadict(data, files, name) 
    tc = get_or_create_topic(super_value[0], super_value[1], 
          super_value[2], super_value[3]) 

    if tc: return tc.pk 

    return None 

Très facile lorsque vous arrêtez de creuser et que vous vous demandez si le trou est vraiment nécessaire.

Questions connexes