i'm using django 1.10 and I need to display data and create a filter based on a value from a different model(which has a foreign key referencing my model that is used on the admin template) These are my 2 models: This one is used to generate the template:
class Job(models.Model):
company = models.ForeignKey(Company)
title = models.CharField(max_length=100, blank=False)
description = models.TextField(blank=False, default='')
store = models.CharField(max_length=100, blank=True, default='')
phone_number = models.CharField(max_length=60, null=True, blank=True)
This is the other one that holds a foreign key reference to my first one:
class JobAdDuration(models.Model):
job = models.ForeignKey(Job)
ad_activated = models.DateTimeField(auto_now_add=True)
ad_finished = models.DateTimeField(blank=True, null=True)
Inside my template, I have been able to display the(latest)start and end times
def start_date(self,obj):
if JobAdDuration.objects.filter(job=obj.id).exists():
tempad = JobAdDuration.objects.filter(job=obj).order_by("-id")[0]
return tempad.ad_activated
And then I just call this inside the list_display and that is working fine. However, i have trouble setting a filter field using these criteria.
If I just add it to my list_filter then I get an error that there is no such field inside my model which is true (since that one is in another table that has reference to my job table). So I was wondering what is the right approach to solve this? Do I need to create another function for the filter itself but even then I'm not sure how should I call it inside the list_filter.
Here is a snippet of my Django admin page.
class JobAdmin(admin.OSMGeoAdmin, ImportExportModelAdmin):
inlines = [
]
readonly_fields = ( 'id', "start_date", )
raw_id_fields = ("company",)
list_filter = (('JobAdDuration__ad_activated', DateRangeFilter), 'recruitment', 'active', 'deleted', 'position', ('created', DateRangeFilter), 'town')
search_fields = ('title', 'description', 'company__name', 'id', 'phone_number', 'town')
list_display = ('title', 'id', 'description', 'active', 'transaction_number', 'company', 'get_position', 'town','created', 'expires', 'views', 'recruitment', 'recruits', 'paid', 'deleted', "start_date", "end_Date", "ad_consultant")
def start_date(self,obj):
if JobAdDuration.objects.filter(job=obj.id).exists():
tempad = JobAdDuration.objects.filter(job=obj).order_by("-id")[0]
return tempad.ad_activated
EDIT: In the meantime, I tried to solve it with a simple list filter, but I am unable to get it to work. I would like to place 2 input fields with a calendar(like the default DateRangeFilter) that would represent the start and end time, and then return data based on those values. This is my "prototype" functionality for the simple filter, it works but it returns hard-coded data.
class StartTimeFilter(SimpleListFilter):
title = ('Start date')
parameter_name = 'ad_finished'
def lookups(self, request, model_admin):
#return JobAdDuration.objects.values_list("ad_finished")
return (
('startDate', 'stest1'),
('startDate1', 'test2')
)
def queryset(self, request, queryset):
if not self.value():
return queryset
assigned = JobAdDuration.objects.filter(ad_finished__range=(datetime.now() - timedelta(minutes=45000), datetime.now()))
allJobs = Job.objects.filter(pk__in=[current.job.id for current in assigned])
return allJobs
I would go with customized FieldListFilter
as it allows to bind filter to different model fields based on your requirements.
What is we actually do to implement such filter is next:
expected_parameters
NotImplementedError
Filter code:
class StartTimeFilter(admin.filters.FieldListFilter):
# custom template which just outputs form, e.g. {{spec.form}}
template = 'start_time_filter.html'
def __init__(self, *args, **kwargs):
field_path = kwargs['field_path']
self.lookup_kwarg_since = '%s__gte' % field_path
self.lookup_kwarg_upto = '%s__lte' % field_path
super(StartTimeFilter, self).__init__(*args, **kwargs)
self.form = StartTimeForm(data=self.used_parameters, field_name=field_path)
def expected_parameters(self):
return [self.lookup_kwarg_since, self.lookup_kwarg_upto]
# no predefined choices
def choices(self, cl):
return []
def queryset(self, request, queryset):
if self.form.is_valid():
filter_params = {
p: self.form.cleaned_data.get(p) for p in self.expected_parameters()
if self.form.cleaned_data.get(p) is not None
}
return queryset.filter(**filter_params)
else:
return queryset
Form can be as simple as follows:
class StartTimeForm(forms.Form):
def __init__(self, *args, **kwargs):
self.field_name = kwargs.pop('field_name')
super(StartTimeForm, self).__init__(*args, **kwargs)
self.fields['%s__gte' % self.field_name] = forms.DateField()
self.fields['%s__lte' % self.field_name] = forms.DateField()