Я разбираюсь в этом по частям. Я хочу, чтобы пользователи могли оценивать сообщения в блоге на основе 4 критериев (сложность-рейтинг, рабочая нагрузка-рейтинг, книга-рейтинг, посещаемость-рейтинг). Я использовал выбор в файле models.py, чтобы предоставить пользователям выбор.

После этого, благодаря пользователю из StackOverflow, я смог рассчитать среднее значение для каждого из этих значений и отобразить их в верхней части каждого поста.

Проблема в том, что они отображаются как числа с длинными десятичными числами. Есть ли способ показать человека читаемым вместо этого? Что происходит от моей модели кортежей?

Я сделал оператор {% if%} внутри всего шаблона, жестко запрограммировав его, но я почти уверен, что должен быть лучший способ.

Models.py

class Post(models.Model):
    STATUS_CHOISES = (

        ('draft', 'Draft'),
        ('published', 'Published'),

        )
    category = models.ForeignKey(Category)
    title = models.CharField(max_length=250)
    slug = models.SlugField(max_length=250, unique=True)
    content = models.TextField()
    seo_title = models.CharField(max_length=250)
    seo_description = models.CharField(max_length=160)
    author = models.ForeignKey(User, related_name='blog_posts', default=settings.AUTH_USER_MODEL)
    published = models.DateTimeField(default=timezone.now)
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)
    status = models.CharField(max_length=9, choices=STATUS_CHOISES, default='draft')

    def get_absolute_url(self):
        return reverse('blog:post_detail', args=[self.slug])


    def avg_ratings(self):
        return self.comments.aggregate(
            Avg('difficulty_rating', output_field=FloatField()),
            Avg('workload_rating', output_field=FloatField()),
            Avg('book_rating', output_field=FloatField()),
            Avg('attendance_rating', output_field=FloatField()),
        )


    def __str__(self):
        return self.title



class Comment(models.Model):
    difficulty_rating_choices = (

        (1, 'Very Easy'),
        (2, 'Easy'),
        (3, 'Moderate'),
        (4, 'Hard'),
        (5, 'Very Hard'),

    )

    workload_rating_choices = (

        (1, 'Very Light'),
        (2, 'Light'),
        (3, 'Moderate'),
        (4, 'Heavy'),
        (5, 'Very Heavy'),

    )

    book_rating_choices = (

        (1, '$'),
        (2, '$$'),
        (3, '$$$'),
        (4, '$$$$'),
        (5, '$$$$$'),

    )

    attendance_rating_choices = (

        (1, 'Not Required'),
        (2, 'Required'),


    )
    post = models.ForeignKey(Post, related_name="comments")
    user = models.CharField(max_length=250)
    email = models.EmailField()
    title = models.CharField(max_length=250)
    body = models.TextField()
    created = models.DateTimeField(auto_now_add=True)
    approved = models.BooleanField(default=False)
    difficulty_rating = models.IntegerField(choices=difficulty_rating_choices)
    workload_rating = models.IntegerField(choices=workload_rating_choices)
    book_rating = models.IntegerField(choices=book_rating_choices)
    attendance_rating = models.IntegerField(choices=attendance_rating_choices)


    def approved(self):
        self.approved = True
        self.save()

    def __str__(self):
        return self.title

Views.py

def post_detail(request, slug):
post = get_object_or_404(Post, slug=slug)
template = 'blog/post/post_detail.html'
context = {

    'post': post,

}
return render(request, template, context)

Шаблон

{% with avg_ratings=post.avg_ratings %}
<h1>Difficulty: {{ avg_ratings.difficulty_rating__avg }}</h1>
        <h1>Workload: {{ avg_ratings.workload_rating__avg }}<h1>
        <h1>Book Cost: {{ avg_ratings.book_rating__avg }}<h1>
        <h1>Attendance: {{ avg_ratings.attendance_rating__avg }}<h1>

    {% endwith %}
-1
Darius Mandres 28 Авг 2017 в 00:14

3 ответа

Лучший ответ

Используйте фильтр шаблонов django https://docs.djangoproject.com/ ан / 1,10 / исх / шаблоны / встроенные функции / # floatformat

Например:

{{ avg_ratings.difficulty_rating__avg|floatformat:"-3" }}

Если под «удобочитаемым человеком» вы хотите заменить слова диапазонами значений, создайте свой собственный шаблонный фильтр для этого.

См. https: // docs.djangoproject.com/en/1.11/howto/custom-template-tags/#howto-writing-custom-template-filters

Вы можете сделать что-то вроде (добавить диапазоны для ваших уровней):

from django import template

register = template.Library()

@register.filter
def rating_humanize(value): # Only one argument.
    if value < 1.0:
        return 'Very Easy'
    elif value < 2.0:
        return 'Easy'
    else:
        return 'Hard'

Лучшая реализация - составить список отображений диапазона в строку, а затем найти соответствующий диапазон и вернуть для него строку.

1
ivo 27 Авг 2017 в 21:56

Возможно, использование DecimalField(decimal_places=2) вместо FloatField поможет.

Десятичные документы

0
meili 27 Авг 2017 в 21:28

Не уверен, что это лучшее решение, но это то, что я придумал, вам нужно обновить метод avg_ratings в вашей модели Post:

import math

class Post(models.Model):
    # ...

    def avg_ratings(self):
        ratings = {}
        data = self.comments.aggregate(
            Avg('difficulty_rating', output_field=FloatField()),
            Avg('workload_rating', output_field=FloatField()),
            Avg('book_rating', output_field=FloatField()),
            Avg('attendance_rating', output_field=FloatField()),
        )

        for key, val in data.items():
            key = key.replace('__avg', '')
            scaled_val = int(math.ceil(val))
            display = ''
            choices = getattr(CommentTest, '{}_choices'.format(key))
            for choice_display, choice_val in choices:
                if scaled_val == choice_val:
                    display = choice_display
                    break
            if not display:
                raise Exception('Display for "{}" avg with value "{}" not found.'.format(key, scaled_val))
            ratings[key] = (scaled_val, display, val)
        return ratings

Затем в шаблоне:

{% with avg_ratings=post.avg_ratings %}
  <h1>Difficulty: {{ avg_ratings.difficulty_rating.0 }} - {{ avg_ratings.difficulty_rating.1 }}</h1>
  <h1>Workload: {{ avg_ratings.workload_rating.0 }} - {{ avg_ratings.workload_rating.1 }}<h1>
  <h1>Book Cost: {{ avg_ratings.book_rating.0 }} - {{ avg_ratings.book_rating.1 }}<h1>
  <h1>Attendance: {{ avg_ratings.attendance_rating.0 }} - {{ avg_ratings.attendance_rating.1 }}<h1>
{% endwith %}

Метод avg_ratings делает то, что он подготавливает словарь рейтингов в следующем формате:

{'difficulty_rating': (2, 'Easy', 1.9), ..}

Значением для каждого ключа будет кортеж, содержащий три значения:

  • Первым значением будет масштабированное значение среднего, например, т.е. если в среднем было 1,7, оно будет считаться 2.
  • Второе значение - это удобочитаемое значение дисплея.
  • Третье значение - это фактическое среднее значение, которое было масштабировано.

Я использовал math.ceil в этой логике, но вы можете решить свою собственную логику позже, как вы сказали.

0
Aamir Adnan 27 Авг 2017 в 22:08