python - related - inlines django admin
Django admin agrega filtro personalizado (3)
Esto no es exactamente lo que solicitó, pero podría tener el filtro en JobAdDuration
modelAdmin. De esta forma, puede obtener los trabajos correspondientes filtrados según los campos ad_activated
y ad_finished
. Y he agregado un enlace al campo de job
, para que pueda hacer clic directamente en él para facilitar la navegación.
Para convertirlo en un filtro html5 de fecha, he usado la biblioteca django-admin-rangefilter .
from django.urls import reverse
from django.contrib import admin
from .models import Job, JobAdDuration
from django.utils.html import format_html
from rangefilter.filter import DateRangeFilter
@admin.register(JobAdDuration)
class JobAdDurationAdmin(admin.ModelAdmin):
list_filter = ((''ad_activated'', DateRangeFilter), (''ad_finished'', DateRangeFilter))
list_display = (''id'', ''job_link'', ''ad_activated'', ''ad_finished'')
def job_link(self, obj):
return format_html(''<a href="{}">{}</a>'', reverse(''admin:job_job_change'', args=[obj.job.id]), obj.job.title)
job_link.short_description = ''Job''
Si realmente desea ir a la ruta existente (filtre dentro de JobAdmin
), entonces las cosas se pondrán bastante complicadas.
Estoy usando django 1.10 y necesito mostrar datos y crear un filtro basado en un valor de un modelo diferente (que tiene una clave externa que hace referencia a mi modelo que se usa en la plantilla de administración) Estos son mis 2 modelos: Este es utilizado para generar la plantilla:
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)
Este es el otro que contiene una referencia de clave externa a mi primera:
class JobAdDuration(models.Model):
job = models.ForeignKey(Job)
ad_activated = models.DateTimeField(auto_now_add=True)
ad_finished = models.DateTimeField(blank=True, null=True)
Dentro de mi plantilla, he podido mostrar las (últimas) horas de inicio y finalización
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
Y luego lo llamo dentro de list_display y está funcionando bien. Sin embargo, tengo problemas para configurar un campo de filtro usando estos criterios.
Si simplemente lo agrego a mi list_filter, obtengo un error que indica que no hay un campo de ese tipo dentro de mi modelo que sea verdadero (ya que está en otra tabla que tiene referencia a mi tabla de trabajo). Entonces me preguntaba ¿cuál es el enfoque correcto para resolver esto? ¿Necesito crear otra función para el filtro en sí mismo, pero aún así no estoy seguro de cómo debería llamarlo dentro del list_filter?
Aquí hay un fragmento de mi página de administración de Django.
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
EDITAR: Mientras tanto, traté de resolverlo con un filtro de lista simple, pero no puedo hacer que funcione. Me gustaría colocar 2 campos de entrada con un calendario (como el DateRangeFilter predeterminado) que representaría la hora de inicio y finalización, y luego devolver los datos en función de esos valores. Esta es mi funcionalidad de "prototipo" para el filtro simple, funciona pero devuelve datos codificados.
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
Me gustaría ir con FieldListFilter
personalizado, ya que permite enlazar filtros a diferentes campos de modelo según sus requisitos.
Lo que realmente hacemos para implementar dicho filtro es lo siguiente:
- construye gte y lte lookup_kwargs y especifícalos como parámetros_esperados
- definir opciones para devolver la lista vacía de lo contrario
NotImplementedError
- crear formulario para cuidar la validación de campos
- crear una plantilla personalizada que solo genere un formulario, por ejemplo, {{spec.form}}
- Si el formulario es válido, tome sus datos limpios, filtre Nones y filtre queryset. De lo contrario, haga algo con los errores (en el código de abajo se silencian los errores)
Código del filtro:
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
La forma puede ser tan simple como sigue:
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()
Recientemente he enfrentado un problema similar en el que necesitaba filtrar datos según el valor de otro modelo. Esto se puede hacer usando SimpleListFilter. Solo necesitas un pequeño ajuste en la función de búsqueda y consultas. Le sugeriré que instale la barra de herramientas de depuración de django para que pueda saber qué consultas de SQL se ejecutan internamente por django.
#import your corresponding models first
class StartTimeFilter(SimpleListFilter):
title = (''Start date'')
parameter_name = ''ad_finished''
def lookups(self, request, model_admin):
data = []
qs = JobAdDuration.objects.filter() # Note : if you do not have distinct values of ad_activated apply distinct filter here to only get distinct values
print qs
for c in qs:
data.append([c.ad_activated, c.ad_activated]) # The first c.activated is the queryset condition your filter will execute on your Job model to filter data ... and second c.ad_activated is the data that will be displayed in dropdown in StartTimeFilter
return data
def queryset(self, request, queryset):
if self.value():
assigned = JobAdDuration.objects.filter(ad_activated__exact = self.value()) # add your custom filter function based on your requirement
return Job.objects.filter(pk__in=[current.job.id for current in assigned])
else:
return queryset
y en list_filter
list_filter = (StartTimeFilter) # no quotes else it will search for a field in the model ''job''.