I need to override variables (or pass dynamic data) to imported class.
filters.py
import django_filters
from .models import Gate, Tram, OperationArea, Bogie
from distutils.util import strtobool
from django import forms
class GateFilter(django_filters.FilterSet):
# Prepare dynamic lists with choices
tram_list = [(id, number) for id, number in Tram.objects.all().values_list('id', 'number')]
bogie_list = [(id, number) for id, number in Bogie.objects.all().values_list('id', 'number')]
area_list = [(id, area) for id, area in OperationArea.objects.all().values_list('id', 'area')]
# Generate fields
tram = django_filters.MultipleChoiceFilter(choices=tram_list, label=u'Tramwaj')
car = django_filters.MultipleChoiceFilter(choices=Gate.CAR_SYMBOLS, label=u'Człon')
bogie = django_filters.MultipleChoiceFilter(choices=bogie_list, label=u'Wózek')
bogie_type = django_filters.MultipleChoiceFilter(choices=Gate.BOGIE_TYPES, label=u'Typ wózka')
area = django_filters.MultipleChoiceFilter(choices=area_list, label=u'Obszar')
operation_no = django_filters.CharFilter(label=u'Numer operacji', widget=forms.TextInput(attrs={'size': '16px'}))
status = django_filters.MultipleChoiceFilter(choices=Gate.GATE_STATUSES, label=u'Status')
rating = django_filters.MultipleChoiceFilter(choices=Gate.GATE_GRADES, label=u'Ocena')
class Meta:
pass
views.py
from .filters import GateFilter
class GateListView(generic.ListView):
queryset = None
gate_type = None
template_name = 'qapp/gate/list.html'
context_object_name = 'gate_list'
paginate_by = 20
def get_queryset(self):
# Type is stored in database as big-letter word, so 'bjc' != 'BJC'.
if self.gate_type.upper() == 'BJW':
ordering = ['bogie', 'bogie_type']
else:
ordering = ['tram', 'car']
queryset = Gate.objects.filter(type=self.gate_type.upper()).order_by(*ordering)
self.gate_list = GateFilter(self.request.GET, queryset=queryset)
return self.gate_list.qs.distinct()
def get_context_data(self, **kwargs):
context = super(GateListView, self).get_context_data(**kwargs)
# Return Gate.type to template.
context['gate_type'] = self.gate_type
# Return object (for generating form) to template.
context['gate_list_filter'] = self.gate_list
return context
As you can see, in the filters.py, the data for variables tram_list, bogie_list and area_list are dynamic (fetched from database).
But during importing this class to views.py, this data becomes static.
I tried to override this values:
using #classmethod decorator in class GateFilter, and calling it
before setting self.gate_list object,
in views.py using GateFilter.tram_list (and the rest) notation,
No luck.
I can't use reload() function, due to import type (from .filters import GateFilter).
Currently for update lists in filters.py I need to rerun whole app.
This is unacceptable for business logic of my app.
This is the wrong approach. Rather, you should be using the filters that are aware of querysets and that evaluate them when required: ModelChoiceFilter and ModelMultipleChoiceFilter.
class GateFilter(django_filters.FilterSet):
team = django_filters.ModelMultipleChoiceFilter(queryset=Tram.objects.all())
Related
I do have a rather simple FilterSet that I want to use on a queryset with annotations, but the issue is that it always return an empty result for some reason.
This is the filter I'm having issues with
class BaseGroupFilter(django_filters.FilterSet):
joined = django_filters.BooleanFilter(lookup_expr='exact')
class Meta:
model = Group
fields = dict(id=['exact'],
name=['exact', 'icontains'],
direct_join=['exact'])
And this is the service:
def group_list(*, fetched_by: User, filters=None):
filters = filters or {}
joined_groups = Group.objects.filter(id=OuterRef('pk'), groupuser__user__in=[fetched_by])
qs = _group_get_visible_for(user=fetched_by).annotate(joined=Exists(joined_groups)).all()
return BaseGroupFilter(filters, qs).qs
I want to implement a Django Rest Framework view that returns the dependencies tree of a model instance object. This is the code for such view:
class RoomTypeDependencies(viewsets.ViewSet):
def list(self, request, pk):
room_type = models.RoomType.objects.get(pk=pk)
dependency_tree = self.get_object_dependencies(room_type)
return Response(dependency_tree)
def get_object_dependencies(self, instance):
fields = instance.__class__._meta.get_fields()
dependencies_to_return = []
for field in fields:
print(field.name)
if field.__class__.__name__ == 'ManyToOneRel':
dependency_to_return = []
dependent_instances = getattr(instance, field.name)
for dependent_instance in dependent_instances:
dependency_to_return.append(self.get_object_dependencies(dependent_instance))
dependencies_to_return.append({field.__class__.__name__: dependency_to_return})
return Response({str(instance): dependencies_to_return})
Everything seems to work, but I expected getattr(instance, field.name) to return the dependent instances corresponding to the reverse relationship, just like using model_object_instance.reverse_relationshio_name pattern, but it returns a RelatedManager object instead. The problem in my case is that I have the reverse relationship name in a string variable (field.name).
I have two plugins ProductSelector(parent) and SpecificationSelector(child). I want to set the child up so that when you add it to the parent the Specifications that are shown are the only ones for the product (parent). Right now it pulls in all the specifications from the table. These lines let me filter the data to get what I want.
edit: I found an error that i fixed in the code. I had the PluginBase names the same as the model. This allowed me to use ProductSelector.objects.get(cmsplugin_ptr=instance.parent) in the child to get the parent instance. I still need to figure out how to pass the filtered specification list to the "PluginAdmin Interface"
product = ProductSelector.objects.get(cmsplugin_ptr=instance.parent)
specification = Specifications.objects.filter(product_name__product_name__iexact = product.product_name)
However, I haven't figured out how to send that filtered list to the plugin admin interface.
class ProductSelectorPlugin(CMSPluginBase):
model = ProductSelector
name = "Product Selector"
render_template = "product_selector.html"
allow_children = True
child_classes = ['SpecificationSelectorPlugin']
def render(self, context, instance, placeholder):
context['instance'] = instance
return context
plugin_pool.register_plugin(ProductSelectorPlugin)
class SpecificationSelectorPlugin(CMSPluginBase):
model = SpecificationSelector
render_template = "specification_selector.html"
formfield_overrides = {models.ManyToManyField: {'widget': CheckboxSelectMultiple},}
def render(self, context, instance, placeholder):
product = ProductSelector.objects.get(cmsplugin_ptr=instance.parent)
specification = Specifications.objects.filter(product_name__product_name__iexact = product.product_name)
context['instance'] = instance
return context
plugin_pool.register_plugin(SpecificationSelectorPlugin)
models.py
class ProductSelector(CMSPlugin):
product_name = models.ForeignKey(Product, help_text = "Select the product you want to place")
new_product = models.BooleanField(blank=True)
class SpecificationSelector(CMSPlugin):
specification = models.ManyToManyField(Specifications, blank=True)
def __unicode__(self):
return unicode(self.specification)
Here is an screenshot the Django-cms plugins in the placeholder. Currently it is showing all specs in the table, but I just want it to be the specs for that particular product.
http://imgur.com/3R1LobC
Thank you in advance for the help.
CMSPluginBase inhertis from ModelAdmin which means that you can override the form rendered when adding and editing your plugin.
So you can create a ModelForm subclass like so:
class SpecificationSelectorPluginForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(SpecificationSelectorPluginForm, self).__init__(*args, **kwargs)
if self.instance.parent_id:
# Assume that the parent is a product instance
parent_plugin = self.instance.parent
product = parent_plugin.get_plugin_instance()[0]
if product:
# It's possible that product is an orphan plugin.
specifications = Specifications.objects.filter(
product_name__product_name__iexact=product.product_name)
self.fields['specification'].queryset = specifications
then change your SpecificationSelectorPlugin to use this form like so:
class SpecificationSelectorPlugin(CMSPluginBase):
form = SpecificationSelectorPluginForm
The above will only work if the specification plugin is a direct child of the product plugin.
Unfortunatelly Django doesn't have super-magic Drupal's analog for Views module https://www.drupal.org/project/views (by the way other cms also doesn't have it) so we all need write views in code and add content filters like everyone see in Django Admin by hand.
I need to add filters with dropdowns for Charfield and datepopup widget for DateTime field in my class-based-view, i found django-filter for this http://django-filter.readthedocs.org/en/latest/usage.html
But in docs no example how to setup it with CBW, only with function views.
views.py:
class VkwallpostListView(ListView):
model = Vkwallpost
context_object_name = "vk_list"
def get_template_names(self):
return ["vk_list.html"]
def get_context_data(self, **kwargs):
articles = Vkwallpost.objects.order_by("-date_created")[:5]
videos = Fbpagepost.objects.order_by("-date_created")[:5]
items = list(articles) + list(videos)
items.sort(key=lambda i: i.date_created, reverse=True)
return {"vk_fb_list": items[:5]}
def get_queryset(self):
wallposts = Vkwallpost.objects
if 'all_posts' not in self.request.GET:
pass
elif 'all' in self.request.GET:
pass
else:
success = False
criteria = {}
if 'sentiment' in self.request.GET:
criteria['sentiment'] = self.request.GET['sentiment']
print(criteria)
wallposts = wallposts.filter(**criteria)
return wallposts
And i want to easily add this filters:
import django_filters
class VkwallpostFilter(django_filters.FilterSet):
class Meta:
model = Vkwallpost
fields = ['sentiment', 'date_created']
How to achieve this?
Try to use Django Form with ModelChoiceField or ModelMultipleChoiceField.
Its all that you need.
I want to be able to sort by several custom methods in Django Admin. This question provides solution for one method only.
I tried to modify it:
from django.db import models
class CustomerAdmin(admin.ModelAdmin):
list_display = ('number_of_orders','number_of_somevalue') # added field
def queryset(self, request):
qs = super(CustomerAdmin, self).queryset(request)
qs = qs.annotate(models.Count('order'))
qs = qs.annotate(models.Count('somevalue')) # added line
return qs
def number_of_orders(self, obj):
return obj.order__count
number_of_orders.admin_order_field = 'order__count'
def number_of_somevalue(self, obj): # added method
return obj.somevalue__count
number_of_somevalue.admin_order_field = 'somevalue__count'
and it works incorrectly. It seems that it multiplies the count values instead of counting them separately.
Example:
I have 2 orders and 2 somevalues, but in the panel I see 4 orders and 4 somevalues.
Adding another method with yet another value makes it 8 (2*2*2).
How can I fix it?
You can try this to sort by many custom methods (Tested):
from django.db.models import Count
class CustomerAdmin(admin.ModelAdmin):
# The list display must contain the functions that calculate values
list_display = ('number_of_orders','number_of_somevalue') # added field
# Overwrite queryset in model admin
def queryset(self, request):
qs = super(CustomerAdmin, self).queryset(request)
# The query have to return multiple annotation, for this use distinct=True in the Count function
qs = qs.annotate(number_orders = Count('order', distinct=True)).annotate(number_somevalue = Count('somevalue',distinct=True))
return qs
# This function return the new field calculated in queryset (number_orders)
def number_of_orders(self, obj):
return obj.number_orders
number_of_orders.admin_order_field = 'numberorders' # sortable new column
# And this one will return the another field calculated (number_somevalue)
def number_of_somevalue(self, obj): # added method
return obj.number_somevalue
number_of_somevalue.admin_order_field = 'number_somevalue'# sortable new column