Search code examples
djangodjango-formsjquery-autocompletedjango-ajax-selects

django-ajax-selects app: How do I create a new object when there isn't already one in the database?


I'm using django-ajax-selects, which is a freely available django app providing jquery autocomplete functionality.

I've got it working - i.e. it is autocompleting the form fields I want it to. But I have a problem... I'm using it in a ModelForm which adds Partnership objects to the database:

class Skater(models.Model):
    name = models.CharField(max_length=64)
    surname = models.CharField(max_length=64)
    gender = models.CharField(max_length=1, choices=GENDER_CHOICES)

class Partnership(models.Model):
    female_partner = models.ForeignKey(Skater, limit_choices_to = {'gender': FEMALE}, related_name='female_partner_set')
    male_partner = models.ForeignKey(Skater, limit_choices_to = {'gender': MALE}, related_name='male_partner_set')

I want the user to be able to put in a name and surname into the female_partner and male_partner field even if a Skater object like that doesn't exist and I want that object created. How do I go about doing this? I cannot put the code in the form's save method because the field won't validate (it's not a valid Skater).

EDIT 1: Adding in more code...

The form:

class PartnershipAddForm(forms.ModelForm):
    female_partner = AutoCompleteSelectField('female_skater',required=True)
    male_partner = AutoCompleteSelectField('male_skater',required=True)

    class Meta:
        model = Partnership

settings.py:

AJAX_LOOKUP_CHANNELS = {
    'female_skater' : ('skaters.lookups', 'FemaleLookup'),
    'male_skater' : ('skaters.lookups', 'MaleLookup'),
}

lookups.py (MaleLookup is the same except that gender=MALE):

class FemaleLookup(object):

    def get_query(self,q,request):
        """ return a query set.  you also have access to request.user if needed """
        return Skater.objects.filter(Q(gender=FEMALE) & (Q(name__istartswith=q) | Q(surname__istartswith=q)))

    def format_item(self,skater):
        """ simple display of an object when it is displayed in the list of selected objects """
        return unicode(skater)

    def format_result(self,skater):
        """ a more verbose display, used in the search results display.  may contain html and multi-lines """
        return "%s<br/>" % unicode(skater)

    def get_objects(self,ids):
        """ given a list of ids, return the objects ordered as you would like them on the admin page.
            this is for displaying the currently selected items (in the case of a ManyToMany field)
        """
        return Skater.objects.filter(pk__in=ids).order_by('name','surname')

Solution

  • AutoCompleteSelectField holds the id of the object rather than the text, which is why I had the "required" error all along (and why Daniel's solution doesn't work). The value variable was empty as a Skater that doesn't exist doesn't have an id.

    I'm not sure this is the best way of doing this, but I ended up using AutoCompleteField instead of AutoCompleteSelectField. AutoCompleteField holds text, but it doesn't create a Skater object for me.

    The code:

    class PartnershipAddForm(forms.ModelForm):
        female_partner = AutoCompleteField('female_skater',required=True)
        male_partner = AutoCompleteField('male_skater',required=True)
    
        class Meta:
            model = Partnership
    
        def save(self):
            partners = [self.cleaned_data['female_partner'],
                        self.cleaned_data['male_partner']]
            name = ['','']
            surname = ['','']
            for i in [0,1]:
                name[i],surname[i] = get_name_surname(partners[i])
            partners = [None,None]
            partners_created = [None,None]
            gender = [FEMALE,MALE]
            for i in [0,1]:        
                partners[i],partners_created[i] = Skater.objects.get_or_create(
                                                name=name[i],
                                                surname=surname[i],
                                                gender=gender[i]
                                            )
    
             partnership, created = Partnership.objects.get_or_create(
                                        female_partner=partners[0],
                                        male_partner=partners[1],
                                    )
             return partnership