python - template - related field got invalid lookup: icontains




Django admin agrega filtro personalizado (2)

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()




django-admin-filters