Disabling options in django-autocomplete-light - django

Just started using django-autocomplete-light (autocomplete.ModelSelect2) and while I have managed to get it working, I wondered if it is possible to pass disabled options?
I have a list of customers to choose from but some, for various reasons, shouldn't be selected they shouldn't be able to use them. I know I could filter these non-selectable customers out, but this wouldn't be very usable as the user might think that the customer isn't in the database. If so, could someone point me in the right direction as I'm not sure where to start.
It says in the Select2 documentation that disabling options should be possible. Presumably if I could also send a 'disabled':true within the returned json response that might do it.

OK, so here is what I came up with and it works.
view.py
The Select2ViewMixin is subclassed and then a 'disabled' attribute is added to the customer details. This value provided by the ParentAutocomplete view.
from dal import autocomplete
from dal_select2.views import Select2ViewMixin
from dal.views import BaseQuerySetView
class CustomSelect2ViewMixin(Select2ViewMixin):
def get_results(self, context):
return [
{
'id': self.get_result_value(result),
'text': self.get_result_label(result),
'selected_text': self.get_selected_result_label(result),
'disabled': self.is_disabled_choice(result), # <-- this gets added
} for result in context['object_list']
]
class CustomSelect2QuerySetView(CustomSelect2ViewMixin, BaseQuerySetView):
"""Adds ability to pass a disabled property to a choice."""
class ParentAutocomplete(CustomSelect2QuerySetView):
def get_queryset(self):
qs = Customer.objects.all()
if self.q:
qs = qs.filter(org_name__icontains=self.q)
return qs.order_by('org_name', 'org_city')
def get_result_label(self, item):
return item.selector_name
def get_selected_result_label(self, item):
return item.selector_name
def is_disabled_choice(self, item): # <-- this is where we determine if the record is selectable or not.
customer_id = self.forwarded.get('customer_id', None)
return not (item.can_have_children and not str(item.pk) == customer_id)
form.py
The form is then used as normal.
from dal import autocomplete
class CustomerBaseForm(forms.ModelForm):
customer_id= forms.IntegerField(required=False, widget=forms.HiddenInput)
class Meta:
model = Customer
widgets = {
'parent':autocomplete.ModelSelect2(
url='customer:parent-autocomplete',
forward=['customer_id'],
)
}
Hopefully this might be useful to someone.

Related

How to use the django-admin-search with search_FieldNameHere ? I am unable to use the same

The django-admin-search plugin version 0.3.5 has beem installed with django 3.0.5 on linux. How exactly to use the search_field query, I fail to understand. I am not great working with OOPS yet.
What I tried:
def search_mfrom(request, field_value, param_values):
"""
intercept query filter for description field
"""
query = Q()
#print(request, field_value, param_values, 'llllllllllllll')
query = Q(mfrom='sowmiya#abc.com')
print(query, 'qqqqqqqqqqqq')
return query
The print(query, 'qqqqqqqqqqq') never executes. Am I passing in something wrong? The form field is mfrom.
Secondly, wish to send in login specific records in queryset which this tool is overriding. How to do that? I am able to override the queryset based on login domain part of the request.user, but since this tool is also overriding the queryset object of the modeladmin, I don't know how to go about. The code snippet for the same:
def get_queryset(self, request):
domain_list = []
temp = []
qs = super(LogSearchFormAdmin, self).get_queryset(request)
if request.user.is_superuser:
return qs
else:
domain_list = Domain.objects.filter(
customer__in=Customer.objects.filter(
email=request.user.username)).values_list(
'name', flat=True)
#print(list(domain_list))
dom_names = list(domain_list)
#print(dom_names)
qs = MailLogs.objects.none()
if dom_names:
for d in dom_names:
qs = MailLogs.objects.filter(
Q(mfrom__icontains=d)|Q(mto__icontains=d))
# qs |= MailLogs.objects.filter(mfrom__icontains=d)
# qs |= MailLogs.objects.filter(mto__icontains=d)
print(qs.query)
#print(qs)
return qs
I believe it is a mistake in the documentation for this case, I will update it coming soon.
I am not near my development laptop in this week, for this reasson I try to help you without testing code, sorry for this.
Another details is: I create this lib for Django 2, and in this pandemic a lot has happened, international change, visa details, search for house, new job, and more, it's crazy days. And I not have certain if works with Django 3. In next month I check this (I believe I will have solved everything)
But if works, all your need is:
Install a lib (check GitHub page)
Create your search form
from django.forms import Form
from django.forms import CharField
class YourFormSearch(Form):
mfrom = CharField() ## this type is just for registry will be ignored because your override this
Override in the admin
class YourAdmin(AdvancedSearchAdmin):
def search_mfrom(self, field, field_value, form_field, request, param_values):
"""
intercept mfrom filter and override
"""
query = Q()
# your Q logic here
return query

Trying to filter by user group using class based view with django-tables2, can't access self.user

I'm trying to use a class based view using django-tables2 to define the table and template returned based on what group the logged in user belongs to.
This is my attempt at doing so:
class cases(LoginRequiredMixin, SingleTableView):
login_url = '/account/login/'
if User.objects.filter(pk=self.request.user.id, groups__name='teachers').exists():
model = Graduation
table_class = TeachersTable
template_name = 'mysite/teachers.html'
elif User.objects.filter(pk=self.request.user.id, groups__name='students').exists():
model = Graduation
table_class = StudentsTable
template_name = 'mysite/students.html'
I think the approach is more or less correct (I've only learned about class based views today), but I am unsure how to access the user id.
The relevant parts of this view should only be called when a user is logged in (at least I think) because I'm using the LoginRequiredMixin, so 'self' should exist.
The answers I have seen addressing this problem say override get_queryset, but I am reluctant to do that as I think that will break django-tables2.
What is the best approach in this case to do what I am trying to do?
There are a few things going on here.
First, all code in a class runs when the module is loaded, not when the view is run. So your code is running at the wrong time. Partially because of this, you don't have access to self.
self isn't that magic, it doesn't appear from nowhere, you can only use it in class methods:
class Foo:
self.spam = "eggs" # Wrong, no self here.
class Bar:
def set_spam(self): # self is the instance of the class.
self.spam = "eggs" # Works.
I'm not sure what table_class is, from a search it looks like it comes from django-tables, so there's a bit of guesswork here. It seems like you want something like this:
class GraduationCaseView(LoginRequiredMixin, SingleTableView):
model = Graduation
def get_template_names(self):
if self.request.user.groups.filter(name='teachers').exists():
return ['mysite/teachers.html']
return 'mysite/students.html'
def get_table_class(self):
if self.request.user.groups.filter(name='teachers').exists():
return TeachersTable
return StudentsTable
This should work. There is an issue with this: you'd be doing the same database query twice. There are some ways around this, but it requires knowing a bit about CBVs and their execution order. Since I'm not sure what SingleTableView is doing, this may or may not work:
class GraduationCaseView(LoginRequiredMixin, SingleTableView):
model = Graduation
def get_queryset(self):
qs = super().get_queryset()
if self.request.user.groups.filter(name='teachers').exists():
self.group = 'teachers'
else:
self.group = 'students'
return qs
def get_template_names(self):
if self.group == 'teachers':
return ['mysite/teachers.html']
return 'mysite/students.html'
def get_table_class(self):
if self.group == 'teachers':
return TeachersTable
return StudentsTable
You should probably read up on Python's documentation of classes, too, so you understand how they work.
One more thing, you don't need to set login_url on your view if settings.LOGIN_URL is set.

Customize queryset in django-filter ModelChoiceFilter (select) and ModelMultipleChoiceFilter (multi-select) menus based on request

I'm using django-filter in 2 places: My Django Rest Framework API, and in my FilterViews (Django Filter's Generic ListViews.) In the case of my FilterViews I'm showing both select boxes (ModelChoiceFilter) and multi-select boxes (ModelMultipleChoiceFilter) to be filtered on.
I need to be able to limit what's in those select and multi-select inputs based on a field inside the request.
It's relatively simple to change what's listed as a kwarg in the relevant field in the FilterSet. For example, here's my FilterSet where the queryset is set as a kwarg:
class FieldFilter(django_filters.FilterSet):
"""Filter for the field list in the API"""
dataset = ModelChoiceFilter(queryset=Dataset.objects.all())
class Meta(object):
"""Meta options for the filter"""
model = Field
fields = ['dataset']
And it's relatively straightforward to limit what the result is in DRF inside the get_queryset() method. For example, here's my DRF ViewSet:
class FieldViewSet(viewsets.ReadOnlyModelViewSet):
"""A ViewSet for viewing dataset fields"""
queryset = Field.objects.all()
serializer_class = FieldSerializer
filter_class = FieldFilter
def get_queryset(self):
"""Get the queryset"""
queryset = super(FieldViewSet, self).get_queryset()
queryset = queryset.filter(
dataset__organization=self.request.organization)
return queryset
I just can't find anywhere to edit the Dataset field in the filter_class when the view is being displayed.
This is super straightforward in Django FormView generic views, but it doesn't appear that FieldViewSet follows the same get_form() structure as generic views. It's also relatively straightforward to do in the admin, but DRF/Django-Filter don't seem to follow that structure either.
Is there any way to customize the queryset in those inputs on a per-request basis? Preferably both on FilterViews and in the HTML API browser, but just in FilterViews would be fine if it's too complicated for the HTML API browser.
After hours of search, I found the solution in the official documentation here!
The queryset argument for ModelChoiceFilter and ModelMultipleChoiceFilter supports callable behavior. If a callable is passed, it will be invoked with the request as its only argument.
import django_filters as filters
from django.utils.translation import gettext as _
def ourBranches(request):
if request is None:
return Branch.objects.none()
company = request.user.profile.company
return Branch.objects.filter(company=company)
class UnitFilter(filters.FilterSet):
branch = filters.ModelChoiceFilter(
queryset=ourBranches, empty_label=_("All Branches"))
class Meta:
model = Unit
fields = ('branch', )
and in the view, I made sure to pass the request as well
qs = Unit.objects.all()
filter = UnitFilter(self.request.GET, request=self.request, queryset=qs)
table = UnitTable(filter.qs)
I also had problems finding a resolution to this.
I solved it (I think) via the following:
views.py
table_filter = ExampleFilter(request.GET, kwarg_I_want_to_pass=request.user, queryset=qs)
filters.py
class ExampleFilter(django_filters.FilterSet):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('kwarg_I_want_to_pass', None)
super(ExampleFilter, self).__init__(*args, **kwargs)
self.filters['field_to_filter'].extra.update({
'queryset': Supplier.objects.filter(related_user=self.user),
'empty_label': '',
'help_text': False
})
class Meta:
model = ExampleModel
fields = ['related_user', 'field_to_filter', ... other fields]

Django: how to use custom manager in get_previous_by_FOO()?

I have a simple model MyModel with a date field named publication_date. I also have a custom manager that filters my model based on this date field.
This custom manager is accessible by .published and the default one by .objects.
from datetime import date, datetime
from django.db import models
class MyModelManager(models.Manager):
def get_query_set(self):
q = super(MyModelManager, self).get_query_set()
return q.filter(publication_date__lte=datetime.now())
class MyModel(models.Model):
...
publication_date = models.DateField(default=date.today())
objects = models.Manager()
published = MyModelManager()
This way, I got access to all objects in the admin but only to published ones in my views (using MyModel.published.all() queryset).
I also have
def get_previous(self):
return self.get_previous_by_publication_date()
def get_next(self):
return self.get_next_by_publication_date()
which I use in my templates: when viewing an object I can link to the previous and next object using
{{ object.get_previous }}
The problem is: this returns the previous object in the default queryset (objects) and not in my custom one (published).
I wonder how I can do to tell to this basic model functions (get_previous_by_FOO) to use my custom manager.
Or, if it's not possible, how to do the same thing with another solution.
Thanks in advance for any advice.
Edit
The view is called this way in my urlconf, using object_detail from the generic views.
(r'^(?P<slug>[\w-]+)$', object_detail,
{
'queryset': MyModel.published.all(),
'slug_field': 'slug',
},
'mymodel-detail'
),
I'm using Django 1.2.
In fact, get_next_or_previous_by_FIELD() Django function (which is used by get_previous_by_publication_date...) uses the default_manager.
So I have adapted it to reimplement my own utility function
def _own_get_next_or_previous_by_FIELD(self, field, is_next):
if not self.pk:
raise ValueError("get_next/get_previous cannot be used on unsaved objects.")
op = is_next and 'gt' or 'lt'
order = not is_next and '-' or ''
param = smart_str(getattr(self, field.attname))
q = Q(**{'%s__%s' % (field.name, op): param})
q = q|Q(**{field.name: param, 'pk__%s' % op: self.pk})
qs = MyModel.published.filter(q).order_by('%s%s' % (order, field.name), '%spk' % order)
try:
return qs[0]
except IndexError:
def get_previous(self):
return self._own_get_next_or_previous_by_FIELD(MyModel._meta.fields[4], False)
def get_next(self):
return self._own_get_next_or_previous_by_FIELD(MyModel._meta.fields[4], True)
This is not a very clean solution, as I need to hardcode the queryset and the field used, but at least it works.

Django: How to make a query for on object based on an M2M field (multiple selections for field on search form)

I need help coming up with an efficient way to do a search query for a set of objects, based on a M2M field. My search form is going to look something like Blue Cross Blue Shield's | eg: this image
Now, let's suppose my model looks like this:
# models.py
class Provider(models.Model)
title = models.CharField(max_length=150)
phone = PhoneNumberField()
services_offered = models.ManyToManyField(ServiceType)
def __unicode__(self):
return self.title
class ServiceCategory(models.Model):
service_category = models.CharField(max_length=30)
def __unicode__(self):
return self.service_category
class Meta(object):
verbose_name_plural = "Service Categories"
class ServiceType(models.Model):
service_type = models.CharField(max_length=30)
service_category = models.ForeignKey(ServiceCategory)
def __unicode__(self):
return u'%s | %s' % (self.service_category, self.service_type
Also, we have to keep in mind that the options that we select are subject to change, since how they display on the form is dynamic (new ServiceCategories and ServiceTypes can be added at anytime). *How should I go about constructing a query for the Provider objects, given that a person using the search form can select multiple Services_Offered?*
This is currently my HIGHLY INEFFICIENT METHOD:
#managers.py
from health.providers.models import *
from django.db.models import Q
class Query:
def __init__(self):
self.provider_objects=Provider.objects.all()
self.provider_object=Provider.objects
self.service_object=ServiceType.objects
self.category_objects=ServiceCategory.objects.all()
def simple_search_Q(self, **kwargs): #matt's learning note: **kwargs passes any dictionary
return self.provider_objects.filter(
Q(services_offered__service_type__icontains=kwargs['service']),
Q(title__icontains=kwargs['title']),
Q(state=kwargs['state']),
).distinct().order_by('title')
====================
#views.py
from django.shortcuts import render_to_response
from health.providers.models import *
from health.search.forms import *
from health.search.managers import Query #location of the query sets
from django.core.paginator import Paginator, InvalidPage, EmptyPage
from django.template import RequestContext
def simple_search(request):
if request.method == 'POST':
SimpleSearch_form = SimpleSearch(request.POST)
if SimpleSearch_form.is_valid():
request.session["provider_list"] = None
kwargs = {'title': request.POST['title'],
'service': request.POST['service'], 'state': request.POST['state'] }
provider_list = Query().simple_search_Q(**kwargs)
return pagination_results(request, provider_list)
else:
SimpleSearch_form = SimpleSearch()
return render_to_response('../templates/index.html', { 'SimpleSearch_form': SimpleSearch_form},
context_instance=RequestContext(request))
How can I make my query:
Obtain Provider objects based on selecting multiple request.POST['service']
More efficient
Thanks for any help in advanced.
Best Regards,
Matt
1: for multiple request.POST['service'], I assume you mean these are CheckBoxes.
I'd make the CheckBox values ID's, not names, and do a PK lookup.
'services_offered__pk__in': request.POST.getlist('service')
That would return all Provider objects that have ALL of the services selected.
PS: You are also using CapitalCase for instances which is very confusing. If you want your code to be readable, I highly recommend some changes to your style (don't use CapitalCase for instances or variables) and make your variables more descriptive.
SimpleSearch_form = SimpleSearch() # what is SimpleSearch?
simplesearch_form = SimpleSearchForm() # now, it's very clear what the class SimpleSearchForm is
# and the form instance is clearly a for instance.
2: making it more efficient? You could get rid of a lot of code and code separation by remove your whole Query class. Also, I don't know why you are using Q objects since you are not doing anything that would require it (like OR or OR + AND).
def simple_search(request):
if request.method == 'POST':
searchform = SimpleSearchForm(request.POST)
if searchform.is_valid():
request.session['provider_list'] = None
post = request.POST
providers = Provider.objects.filter(services_offered__pk__in=post.getlist('services'),
title=post['title'], state=post['state'])
return pagination_results(request, provider_list)
else:
searchform = SimpleSearchForm()
return direct_to_template(request, '../templates/index.html', { 'searchform': searchform})