yourlabs / django-autocomplete-light

A fresh approach to autocomplete implementations, specially for Django. Status: v4 alpha, v3 stable, v2 & v1 deprecated.
https://django-autocomplete-light.readthedocs.io
MIT License
1.81k stars 468 forks source link

Setting the initial value of a ChoiceField that uses Select2 widget #712

Open geekashu opened 8 years ago

geekashu commented 8 years ago

Hi,

This is very similar to Issue 621.

Models.py

class Step(TimeStampedModel):
    name = models.CharField('Title', max_length=255)
    description = models.TextField() 

    attachment_type = models.ForeignKey(ContentType, limit_choices_to={'model__in': ('formentry', 'document', 'tutorial',)},
                                        null=True, blank=True, on_delete=models.SET_NULL, help_text="Select attachment type if applicable.")
    attachment = models.PositiveIntegerField(null=True, blank=True)
    attachment_object = GenericForeignKey('attachment_type', 'attachment')

    project = models.ForeignKey(Project, on_delete=models.CASCADE)

    def __str__(self):
      return self.name

views.py

class StepAttachmentAutocomplete(ViewMixin, ListAPIView):
    serializer_class = AutocompleteSerializer
    permission_classes = (IsAuthenticated,)
    pagination_class = AutocompletePagination

    def get_queryset(self):
        qs = ()
        if hasattr(self, "forwarded"):
            pk = self.forwarded.get('attachment_type', None)
            if pk: 
                ct = str(ContentType.objects.get(pk=pk))
                if ct == 'Form':
                    qs = FormEntry.objects.all()
                elif ct == 'Tutorial':
                    qs = Tutorial.objects.all()
                else:
                    qs = Document.objects.all()

                self.q = self.request.query_params.get('q', None)
                if self.q:
                    qs = qs.filter(name__istartswith=self.q)

        return qs

forms.py

class StepForm(forms.ModelForm):
    attachment = forms.IntegerField(required=False,
                    widget=autocomplete.Select2(url='attachment-autocomplete', forward=['attachment_type'])
                 )

    class Meta:
        model = Step 
        fields = ('__all__')

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

        if self.instance.attachment:
            obj = self.instance.attachment_type.get_object_for_this_type(pk=self.instance.attachment)
            self.fields['attachment'].initial = {self.instance.attachment: str(obj),}

Now everything works great except when it come to displaying the saved attachment. Since i can't use foreignkey field for attachment (GenericForeignKey is there), as suggested in the previous answer, how can i use to_python approach to solve this. Attachment field is coming blank though pk for the field is being saved in the DB.

jpic commented 8 years ago

Yes, that is why you cant use standard django stuff, double check the docs for GfK support there is a trick. I'll be able to link you when I get to a computer On Jul 21, 2016 6:31 PM, "geekashu" notifications@github.com wrote:

Hi,

This is very similar to Issue 621 https://github.com/yourlabs/django-autocomplete-light/issues/621.

Models.py

class Step(TimeStampedModel): name = models.CharField('Title', max_length=255) description = models.TextField()

attachment_type = models.ForeignKey(ContentType, limit_choices_to={'model__in': ('formentry', 'document', 'tutorial',)},
                                    null=True, blank=True, on_delete=models.SET_NULL, help_text="Select attachment type if applicable.")
attachment = models.PositiveIntegerField(null=True, blank=True)
attachment_object = GenericForeignKey('attachment_type', 'attachment')

project = models.ForeignKey(Project, on_delete=models.CASCADE)

def __str__(self):
  return self.name

views.py

class StepAttachmentAutocomplete(ViewMixin, ListAPIView): serializer_class = AutocompleteSerializer permission_classes = (IsAuthenticated,) pagination_class = AutocompletePagination

def get_queryset(self):
    qs = ()
    if hasattr(self, "forwarded"):
        pk = self.forwarded.get('attachment_type', None)
        if pk:
            ct = str(ContentType.objects.get(pk=pk))
            if ct == 'Form':
                qs = FormEntry.objects.all()
            elif ct == 'Tutorial':
                qs = Tutorial.objects.all()
            else:
                qs = Document.objects.all()

            self.q = self.request.query_params.get('q', None)
            if self.q:
                qs = qs.filter(name__istartswith=self.q)

    return qs

forms.py

class StepForm(forms.ModelForm): attachment = forms.IntegerField(required=False, widget=autocomplete.Select2(url='attachment-autocomplete', forward=['attachment_type']) )

class Meta:
    model = Step
    fields = ('__all__')

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

    if self.instance.attachment:
        obj = self.instance.attachment_type.get_object_for_this_type(pk=self.instance.attachment)
        self.fields['attachment'].initial = {self.instance.attachment: str(obj),}

Now everything works great except when it come to displaying the saved attachment. Since i can't use foreignkey field for attachment (GenericForeignKey is there), as suggested in the previous answer, how can i use to_python approach to solve this. Attachment field is coming blank though pk for the field is being saved in the DB.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/yourlabs/django-autocomplete-light/issues/712, or mute the thread https://github.com/notifications/unsubscribe-auth/AAFxrBcSE04li99pLGhqJdJzigpEKI2vks5qX57XgaJpZM4JR-2k .

jpic commented 8 years ago

You need the future model form class as documented here: http://django-autocomplete-light.readthedocs.io/en/master/gfk.html#form-example

geekashu commented 8 years ago

@jpic Thanku... At this line code is like this.

    def filter_choices_to_render(self, selected_choices):
        """Replace self.choices with selected_choices."""
        self.choices = [c for c in self.choices if
                               six.text_type(c[0]) in selected_choices]

Now since i am using Integer field to save my options, it fails when it comes here and try to type cast my saved option to text. It will never match with selected choices since it contains an int.

jpic commented 8 years ago

Are you saying that selected_choices is a list of ints here ? Then you are not using the right form field, because it should be a list of strings containing both the ctype and the object pk for each choice. On Jul 23, 2016 7:12 AM, "geekashu" notifications@github.com wrote:

@jpic https://github.com/jpic Thanku... At this line https://github.com/yourlabs/django-autocomplete-light/blob/master/src/dal/widgets.py#L59 code is like this.

def filter_choices_to_render(self, selected_choices):
    """Replace self.choices with selected_choices."""
    self.choices = [c for c in self.choices if
                           six.text_type(c[0]) in selected_choices]

Now since i am using Integer field to save my options, it fails when it comes here and try to type cast my saved option to text. It will never match with selected choices since it contains an int.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/yourlabs/django-autocomplete-light/issues/712#issuecomment-234700033, or mute the thread https://github.com/notifications/unsubscribe-auth/AAFxrFnGHwTP6us-Z46aO65wS5cOMNOyks5qYaKqgaJpZM4JR-2k .

jpic commented 8 years ago

Right, so don't use that form class, I was mislead because I thought you wanted to use GfK like in our documentation, but you are instead trying to have 2 fields in to implement GfK support your own way with your own code.

In this case, don't use future model form, and don't try to use to_python for initial provisioning. Do the provisioning inside your form class, in init().

Hope this helps.

Otherwise, if you just want to get this down and move on to something else that matters I recommend you follow our docs for GfK.

geekashu commented 8 years ago

@jpic Hey thanks for this. I ended up using doc way of GFK implementation. Though i have created a API view using DRF to render JSON objects rather than using Select2QuerySetSequenceView. Also i will be using haystack to get the results for this auto-complete view. This will give better performance as overhead on DB will be less.

jpic commented 8 years ago

Pretty cool, enjoy, create awesome stuff, share it ;)