Combining filters in django - python

I am using django_filters and I have a little problem with combining it each other.
What do I have? Basic filters, for example:
class BasicFilter(django_filters.FilterSet):
class Meta:
model = myModel
fields = []
class TimeFilter(BasicFilter):
created = django_filters.DateFromToRangeFilter(
help_text='Date from - to', label='Time'
)
class Meta(BasicFilter.Meta):
fields = ['created']
class AgentFilter(BasicFilter):
agent = django_filters.ModelMultipleChoiceFilter(
queryset=AgentClass.objects.all(), help_text=''
)
class Meta(BasicFilter.Meta):
fields = ['agent']
class SomethingElseFilter(BasicFilter):
something = django_filters.ModelMultipleChoiceFilter(
queryset=SomethingElse.objects.all(), help_text=''
)
class Meta(BasicFilter.Meta):
fields = ['something']
The user will decide which filters he wants, e.g. He will choose TimeFilter and AgentFilter, and I need to connect this basic filters to one ConnectedFilter.
Then I handle it in my views, e.g.
class MyView(ListView):
model = myModel
template_name = "filters.html"
def get_queryset(self):
qs=MyModel.objects.all()
try:
self.connected_filter = ConnectedFilter(
self.request.GET, queryset = qs)
return self.connected_filter
except:
return qs
def get_context_data(self, **kwargs):
context = super(MyView, self).get_context_data(**kwargs)
context['filter_form'] = self.connected_form.as_table()
return context
Or maybe there's some better solution.

Related

How to serialize ManyToManyField

I want to serialize ManyToManyField but at the same time, I am looking for something which updates the same using ModelViewSet. I am able to serialize it but when I am updating it I am not able to. I know I can make a separate API for that but due to some requirements, I need to stick to one endpoint. Here is my code
class ComponentSerializers(serializers.ModelSerializer):
class Meta:
model = coreModel.Component
fields = '__all__'
class MonitorSerializers(serializers.ModelSerializer):
device = ComponentSerializers(read_only=True, many=True)
class Meta:
model = models.Monitor
fields = '__all__'
read_only_fields = ('id', 'created_at', 'updated_at',)
and views.py is
class MonitorViewSet(viewsets.ModelViewSet):
authentication_classes = (authentication.TokenAuthentication,)
permission_classes = (permissions.IsAuthenticated,)
queryset = models.Monitor.objects.all()
filter_backends = (DjangoFilterBackend,OrderingFilter,SearchFilter)
filter_class = superFilter.MonitorFilters
serializer_class = serializers.MonitorSerializers
If you want update ManytoMany or Related objects
Override
def update(self, validated_data):
in MonitorSerializers
class MonitorSerializers(serializers.ModelSerializer):
device = ComponentSerializers(read_only=True, many=True)
device_ids = serializers.ListField(write_only=True,
child = serializers.IntegerField(min_value = 1))
....
def update(self, instance, validated_data):
# Remove component data and save in variable
iscomponentdataexists = 'device_ids' in validated_data
if iscomponentdataexists :
componentdata= validated_data.pop('device_ids')
instance = super().update(instance, validated_data) # Update Monitor Data
# looping through new device_ids list
if iscomponentdataexists :
for deviceid in componentdata:
try:
obj = coreModel.Component.objects.get(id=deviceid)
instance.devices.add(obj)
except coreModel.Component.DoesNotExist:
pass
instance.save()
return instance
Remove read_only=True from device

Django-Filter FilterSet Show Only User Generated Objects

I'm using a django-filter form and it's filtering all objects for 'associated_portfolios' how can I make it so it only shows the user the objects they created?
Error message:
'StatsFilter' object has no attribute 'fields'
Filters.py
class StatsFilter(django_filters.FilterSet):
associated_portfolios = django_filters.ModelMultipleChoiceFilter(queryset=associated_portfolios)
class Meta:
model = Trade
fields = ['type', 'asset', 'symbol', 'broker', 'patterns', 'associated_portfolios']
def __init__(self, request, *args, **kwargs):
super(StatsFilter, self).__init__(*args, **kwargs)
self.fields['associated_portfolios'].queryset = Trade.objects.filter(user=request.user)]
views.py
class StatsView(LoginRequiredMixin, FilterView):
model = Trade
template_name = 'dashboard/stats.html'
filterset_class = StatsFilter
def get_context_data(self, **kwargs):
filter = StatsFilter(self.request.GET, queryset=self.get_queryset())
context = super().get_context_data(**kwargs)
context['filter'] = filter
context['get_users_trades'] = Trade.objects.get_users_trades('tj3admin')
context['get_largest_winning_trade'] = filter.qs.aggregate(max_value=Max('profit_loss_value_fees'))['max_value']
return context
Ah, now I remember: set the queryset argument of ModelMultipleChoiceFilter to a callable that accepts request as it's only argument:
def portfolio_filtered_queryset(request):
return Trade.objects.filter(user=request.user)
class StatsFilter(django_filters.FilterSet):
associated_portfolios = django_filters.ModelMultipleChoiceFilter(queryset=porfolio_filtered_queryset)
The view:
class StatsView(LoginRequiredMixin, FilterView):
model = Trade
template_name = 'dashboard/stats.html'
filterset_class = StatsFilter
def get_context_data(self, **kwargs):
# Must pass in request!
filter = StatsFilter(self.request.GET, queryset=self.get_queryset(), request=self.request)
context = super().get_context_data(**kwargs)
context['filter'] = filter
context['get_users_trades'] = Trade.objects.get_users_trades('tj3admin')
context['get_largest_winning_trade'] = filter.qs.aggregate(max_value=Max('profit_loss_value_fees'))['max_value']
return context
In django_filters.filters.QuerySetRequestMixin.get_request() the request instance is obtained from the parent. But I see no logic in django_filters.filterset.BaseFilterSet or concretes that tries to obtain the request through other means. So you must pass the request to the StatsFilter if you wish to make use of QuerySetRequestMixin.

How to add new field to filtered query

I'm trying to add a new column for which I need to do some operations based on other models in a FilterSet.
I have my view like this:
class FilteredListView(ListView):
filterset_class = None
def get_queryset(self):
queryset = super().get_queryset()
self.filterset = self.filterset_class(self.request.GET, queryset=queryset)
return self.filterset.qs.distinct()
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['filterset'] = self.filterset
querysetPaises = Pais.objects.all().filter(paisActivo=1).order_by('nombrePais')
context['paises']=querysetPaises
return context
class ListadoPartnerView(FilteredListView):
filterset_class = PartnerFilter
paginate_by = 10
model = IngresosPersonas
fields = ['idPlataforma', 'number_plataforma', 'contactoinfo']
template_name = 'usuarios/listadoPartners.html'
And my filter is:
class PartnerFilter(django_filters.FilterSet):
class Meta:
model = IngresosPersonas
fields = ['idPlataforma', 'number_plataforma']
Basically I want to include a new column on the filterset that the template receives which is not included in the model.
I've tried to access the filterset.qs but no luck
Many thanks
You can add non model field to FilterSet but you need manually specify method which will be triggered by this filter:
class PartnerFilter(django_filters.FilterSet):
new_field = django_filters.CharFilter(method="filter_new_field")
class Meta:
model = IngresosPersonas
fields = ['idPlataforma', 'number_plataforma', 'new_field']
def filter_new_field(self, queryset, name, value):
return queryset.filter() # your extra filters here
If you need to add non model field on representation level (template) you can just define property in your model:
class MyMode(models.Model):
#property
def new_field(self):
return "some value"
In template you can access this field like this {{ obj.new_field }}.

Django - selectively query models using pk_url_kwarg

Consider the these two models and view:
models.py
class BHA_List(models.Model):
well = models.ForeignKey(WellInfo, 'CASCADE', related_name='bha_list')
bha_number = models.CharField(max_length=100)
class BHA_overall(models.Model):
bha_number = models.ForeignKey(BHA_List, 'CASCADE', related_name='bha_overall')
drill_str_name = models.CharField(max_length=111)
depth_in = models.CharField(max_length=111)
views.py
class BHA_UpdateView(UpdateView):
model = BHA_overall
pk_url_kwarg = 'pk_alt'
form_class = BHA_overall_Form
To my understanding, pk_url_kwarg = 'pk_alt' will query and return instances of model = BHA_overall.
Let's say that I use a different CBV other than UpdateView, and want to implement two models. So something like this:
model = (BHA_overall, BHA_List). Is there any way that I force my pk_url_kwarg = 'pk_alt' to query and return instances only in BHA_List, but force my get_object() return objects in BHA_overall?? What CBV should I use?
you can use just 'View' and define methods post and get.
about like this:
class SomeView(View):
model = BHA_List
template_name = 'some.html'
def get(request, **kwargs):
overall = BHA_overall.objects.all()
return render(request,
self.template_name,
locals())
def get(request, **kwargs):
return render(request, self.template_name, {})

Python similar method in multiple classes

I'm trying to re-factor my Django app. Here is the repeated code i found annoying :
class EducationInfoViewSet(viewsets.ModelViewSet):
queryset = pf.education_info.objects.all()
serializer_class = EducationInfoSerializer
permission_classes = (VisionUserPermissions, )
def get_queryset(self):
model = pf.education_info
identity = self.request.query_params.get('username',None)
queryset = model.objects.all()
user = User.objects.none()
if identity is not None:
user = User.objects.get(username=identity)
student = get_object_or_404(dbd.user_type, username=user)
queryset = model.objects.filter(username=student)
if not permission_check(self.request,user):
return User.objects.none()
return queryset
class FamilyInfoViewSet(viewsets.ModelViewSet):
queryset = pf.family_info.objects.all()
serializer_class = FamilyInfoSerializer
permission_classes = (VisionUserPermissions, )
def get_queryset(self):
model = pf.family_info
identity = self.request.query_params.get('username',None)
queryset = model.objects.all()
user = User.objects.none()
if identity is not None:
user = User.objects.get(username=identity)
student = get_object_or_404(dbd.user_type, username=user)
queryset = model.objects.filter(username=student)
if not permission_check(self.request,user):
return User.objects.none()
return queryset
So my "get_queryset" functions are identical other than one variable, and i have multiple classes like these two. How should I implement this without repeating myself?
Thanks in advance!
Create a mixing class
class QuerysetMixin(viewsets.ModelViewSet):
def get_queryset(self):
model = getattr(pf, self.field_name)
identity = self.request.query_params.get('username',None)
queryset = model.objects.all()
user = User.objects.none()
if identity is not None:
user = User.objects.get(username=identity)
student = get_object_or_404(dbd.user_type, username=user)
queryset = model.objects.filter(username=student)
if not permission_check(self.request,user):
return User.objects.none()
return queryset
and inherit it
class FamilyInfoViewSet(viewsets.ModelViewSet, QuerysetMixin):
queryset = pf.family_info.objects.all()
serializer_class = FamilyInfoSerializer
permission_classes = (VisionUserPermissions, )
field_name = 'family_info'

Categories