0

I need to access an attribute from a related model (to use as a filter attribute in an admin), but I get this error: 'EventTimeAdmin.list_filter[1]' refers to field 'event__sites' that is missing from model 'EventTime'.

Here is are the relevant classes from models.py:

class Event(models.Model):
    title = models.CharField(max_length=100)
    short_description = models.CharField(max_length=250, blank=True)
    long_description = models.TextField(blank=True)
    place = models.ForeignKey(Place, related_name="events", default=0, blank=True, null=True)
    one_off_place = models.CharField('one-off place', max_length=100, blank=True)
    phone = models.CharField(max_length=40)
    cost_low = models.DecimalField('cost (low)', max_digits=7, decimal_places=2, blank=True, null=True)
    cost_high = models.DecimalField('cost (high)', max_digits=7, decimal_places=2, blank=True, null=True)
    age_lowest = models.PositiveSmallIntegerField('lowest age', blank=True, null=True, help_text='Use 0 for "all ages". Leave blank if no age information is available') # Not every event submits info about age limits
    priority = models.ForeignKey(EventPriority)
    ticket_website = models.URLField('ticket Web site', blank=True)
    posted_date = models.DateTimeField('date entered', default=datetime.datetime.now())
    updated_date = models.DateTimeField('date updated', editable=False, blank=True)
    small_photo = models.ImageField(upload_to='img/events/%Y', blank=True)
    teaser_text = models.TextField(max_length=1000, blank=True)
    is_ongoing_indefinitely = models.BooleanField(db_index=True, help_text="If this box is checked, the event won't be displayed by default in the event search. Users will have to manually select \"Display ongoing events\" in order to display it.")
    is_national_act = models.BooleanField('is a national marquee touring act')
    categories = models.ManyToManyField(EventCategory, related_name="events")
    bands = models.ManyToManyField(Band, related_name="events", blank=True)
    sites = models.ManyToManyField(Site)
    related_links = generic.GenericRelation(RelatedLink)
    generic_galleries = generic.GenericRelation(GalleryRelationsPiece)
    staff_photographer_attending = models.BooleanField('Staff Photographer will be attending')

    index = EventIndex()

    class Meta:
        ordering = ('title',)

    def __unicode__(self):
        return self.title

    def save(self, *args, **kwargs):
        self.updated_date = datetime.datetime.now()
        super(Event, self).save(*args, **kwargs)

    def get_absolute_url(self):
        if self.is_recurring():
            return reverse('ellington_events_ongoing_detail', args=[self.id])
        else:
            next = self.get_next_time()
            if next:
                return next.get_absolute_url()
            else:
                try:
                    last = self.event_times.order_by("-event_date", "start_time")[0]
                    return last.get_absolute_url()
                except IndexError:
                    return reverse('ellington_events_todays_events', args=[])

    @property
    def _sites(self):
        """A property used for indexing which sites this object belongs to."""
        return [s.id for s in self.sites.all()]

    # def start_time(self):
    #     return self.event_times.start_time

    # TODO: Redundant
    def get_absolute_url_recurring(self):
        "Returns the absolute URL for this event's recurring-event page"
        return reverse('ellington_events_ongoing_detail', args=[self.id])

    def is_recurring(self):
        """Returns ``True`` if this event is a recurring event."""
        return bool(self.recurring_event_times.count())

    def get_cost(self):
        "Returns this event's cost as a pretty formatted string such as '$3 - $5'"
        if self.cost_low == self.cost_high == 0:
            return 'Free'
        if self.cost_low == self.cost_high == None:
            return ''
        if self.cost_low == 0 and self.cost_high == None:
            return 'Free'
        elif self.cost_low == None and self.cost_high == 0:
            return 'Free'

        if self.cost_low == self.cost_high:
            cost = '$%.2f' % self.cost_low
        elif self.cost_low == 0 or self.cost_low == None:
            cost = 'Free - $%.2f' % self.cost_high
        elif self.cost_high == 0 or self.cost_high == None:
            cost = '$%.2f' % self.cost_low
        else:
            cost = '$%.2f - $%.2f' % (self.cost_low, self.cost_high)
        return cost.replace('.00', '')

    def get_age_limit(self):
        "Returns this event's age limit as a pretty formatted string such as '21+'"
        if self.age_lowest is None:
            return 'Not available'
        if self.age_lowest == 0:
            return 'All ages'
        return '%s+' % self.age_lowest

    def get_place_name(self):
        "Returns this event's venue, taking into account the one_off_place field."
        if self.one_off_place:
            return self.one_off_place
        if self.place is not None:
            return self.place.name
        else:
            return ""

    def get_next_time(self):
        """
        Return the next (in the future) ``EventTime`` for this ``Event``.
        Assumes the event is not recurring. Returns ``None`` if no next time is
        available.
        """
        now = datetime.datetime.now()
        for et in self.event_times.filter(event_date__gte=now.date()).order_by("event_date", "start_time"):
            try:
                dt = datetime.datetime.combine(et.event_date, et.start_time)
            except TypeError:
                return None
            if dt >= now:
                return et
        return None
        # TODO: proprietary
    def get_lj_category(self):
        """
        Returns either 'Nightlife', 'Music', 'Theater', 'Misc.' or
        'Museums and Galleries' for this event.
        """
        MAPPING = (
            ('Activities', 'Misc.'),
            ('Art', 'Museums and Galleries'),
            ('Children', 'Misc.'),
            ('Community', 'Misc.'),
            ('Culinary', 'Misc.'),
            ('KU calendar', 'Misc.'),
            ('Lectures', 'Misc.'),
            ('Museums', 'Museums and Galleries'),
            ('Performance', 'Misc.'),
        )
        # "Performance | Theater" is a special case.
        for c in self.categories.all():
            if str(c) == "Performance | Theater":
                return 'Theater'

        parent_categories = [c.parent_category for c in self.categories.all()]
        for parent_category, lj_category in MAPPING:
            if parent_category in parent_categories:
                return lj_category

        # Failing that, we must be in either 'Music', which can be either
        # 'Music' or 'Nightlife', depending on the venue.
        if self.one_off_place:
            return 'Music'

        # If it's downtown, it's 'Nightlife'. Otherwise, it's 'Music'.
        if "downtown" in self.place.neighborhood.lower():
            return 'Nightlife'

        return 'Music'

class EventTime(models.Model):
    event = models.ForeignKey(Event, related_name="event_times")
    event_date = models.DateField('date')
    start_time = models.TimeField(blank=True, null=True)
    finish_time = models.TimeField(blank=True, null=True)

    objects = EventTimeManager()

    def long_desc(self):
        return self.event.long_description

    # def sites(self):
    #     return self.event._sites

    # def sites(self):
    #     return self.event.sites

    # @property
    # def _sites(self):
    #     """A property used for indexing which sites this object belongs to."""
    #     return [s.id for s in self.event.sites.all()]

    # def _sites(self):
    #     """A property used for indexing which sites this object belongs to."""
    #     return [s.id for s in self.event.sites.all()]

    # def _sites(self):
    #     """A property used for indexing which sites this object belongs to."""
    #     return [s.id for s in event.sites.all()]

    class Meta:
        ordering = ('event_date', 'start_time')

    def __unicode__(self):
        return u"%s -- %s at %s" % (self.event.title, self.event_date.strftime("%m/%d/%y"), self.event.get_place_name())

    def get_absolute_url(self):
        year = self.event_date.strftime("%Y")
        month = self.event_date.strftime("%b").lower()
        day = self.event_date.strftime("%d")
        return reverse('ellington_events_event_detail', args=[year, month, day, self.event.id])

    def get_time(self):
        "Returns this event's time as a pretty formatted string such as '3 p.m. to 5 p.m.'"
        if self.start_time is not None and self.finish_time is not None:
            return '%s to %s' % (dateformat.time_format(self.start_time, 'P'), dateformat.time_format(self.finish_time, 'P'))
        elif self.start_time is not None:
            return dateformat.time_format(self.start_time, 'P')
        return 'time TBA'

    def get_part_of_day(self):
        """
        Returns a string describing the part of day this event time is taking
        place -- either 'Morning', 'Afternoon', 'Evening' or 'Night'.
        """
        if self.start_time is None:
            return 'Time not available'
        if 3 < self.start_time.hour < 12:
            return 'Morning'
        elif 12 <= self.start_time.hour < 18:
            return 'Afternoon'
        elif 18 <= self.start_time.hour < 21:
            return 'Evening'
        return 'Night'

    def is_in_the_past(self):
        return self.event_date < datetime.date.today()

    def happens_this_year(self):
        return datetime.date.today().year == self.event_date.year

    def get_other_event_times(self):
        "Returns a list of all other EventTimes for this EventTime's Event"
        if not hasattr(self, "_other_event_times_cache"):
            self._other_event_times_cache = [et for et in self.event.event_times.order_by("event_date", "start_time") if et.id != self.id]
        return self._other_event_times_cache

    # TODO: proprietary
    def get_weather_forecast(self):
        "Returns a weather.forecast.DayForecast object for this EventTime"
        try:
            from ellington.weather.models import DayForecast
        except ImportError:
            return None
        if not self.event.place.is_outdoors:
            raise DayForecast.DoesNotExist
        if not hasattr(self, "_weather_forecast_cache"):
            self._weather_forecast_cache = DayForecast.objects.get(full_date=self.event_date)
        return self._weather_forecast_cache

DAY_OF_WEEK_CHOICES = (
    (0, 'Sunday'),
    (1, 'Monday'),
    (2, 'Tuesday'),
    (3, 'Wednesday'),
    (4, 'Thursday'),
    (5, 'Friday'),
    (6, 'Saturday'),
)

The thing is, event__title is accessed from EventTimeAdmin:

    class EventTimeAdmin(admin.ModelAdmin):
        list_display = ('event', 'event_date', 'start_time', 'finish_time','long_desc')
        list_filter = ('event_date','event__sites')
        search_fields = ('event__title', 'event_date')
        ordering = ('-event_date',)

        class Meta:
            model = EventTime

But event__sites is not accessible... why is that?

Edit: You can see my attempts (commented out) to define sites in models.py above. None of these worked.

4

1 回答 1

0

event__sites是一个ManyToManyField

您可以尝试以下方法:

管理员.py

def get_sites(self):
   sites = self.event.sites.all()
   result = u''
   for site in sites:
       result = u'%s - %s' % (result, site.name) # unsure about NAME - is it a valid field of Sites Model?
   return result

get_sites.short_description = u'Sites'


class EventTimeAdmin(admin.ModelAdmin):
    list_filter = ('event_date', get_sites)

admin.site.register(EventTime, EventTimeAdmin)

希望能帮助到你。

于 2012-10-12T06:06:20.033 回答