Django queryset скрывает значение объекта
У меня есть следующая (упрощенная) модель:
class Order(models.Model):
is_anonymized = models.BooleanField(default=False)
billing_address = models.ForeignKey('order.BillingAddress', null=True, blank=True)
Я хочу скрыть адрес_расчета для объектов, где клиент выбрал это, установив is_anonymized=True.
Моим лучшим подходом до сих пор было сделать это в init:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.is_anonymized:
self.billing_address = None
self.billing_address_id = None
Это прекрасно работает в админке, НО...
везде в коде есть select_related-QuerySets для Orders:
queryset = Order._default_manager.select_related('billing_address')
Везде, где используется select_related-querysets, случайно показывается billing_address. В других местах (например, в админке) его нет.
Как я могу обеспечить удаление billing_address везде для объектов с is_anonymized = True?
Я думал о перезаписи набора запросов в менеджере, но не смог перезаписать поле billing_address по условию.
Использование шаблона getter-setter не является хорошим решением, потому что он ломает админку в нескольких местах (есть много атрибутов для маскировки, например, billing_address).
Начну с того, что я не понимаю, зачем вам скрывать информацию от администратора вашей системы. Если только у вас не сложная рабочая среда, где только DBA имеет доступ к такой информации, я честно не вижу в этом смысла.
Чтобы ответить на ваш вопрос...
Чтобы скрыть информацию на странице администратора, один из вариантов - отключить все ссылки и заменить HTML на ссылку редактирования, когда значение is_anonymized
равно False
:
(адаптировано из ответ_1 и ответ_2)
admin.py:
from django.utils.html import format_html
class OrderAdmin(admin.ModelAdmin):
list_display = ['anonymous_address']
def anonymous_address(self, obj):
if not obj.is_anonymized:
return format_html(u'<a href="/admin/app/order/{}/change/">{}</a>', obj.id, obj.billing_address.address)
else:
return ("%s" % ('anonymous'))
def __init__(self, *args, **kwargs):
super(OrderAdmin, self).__init__(*args, **kwargs)
self.list_display_links = None
admin.site.register(Order, OrderAdmin)
Обратите внимание, что при таком решении у администратора все еще есть доступ к BillingAddress
модели, если вы зарегистрировали ее на сайте администратора. В этом случае также необходимо будет переопределить это.
В ваших запросах вы можете агрегировать значения с помощью условных выражений:
views.py:
from core.models import Order
from django.db.models import When, Case
def anonymous_address(request):
orders = Order.objects.annotate(anonymised_address=Case(
When(is_anonymized=True, then=None),
When(is_anonymized=False, then='billing_address'),
)).values('is_anonymized', 'anonymised_address')
context = {'orders': orders}
return render(request, 'anonymous_address.html', context)
anonymous_address.html:
{% block content %}
{% for order in orders %}
Should be anonymous: {{order.is_anonymized}} <br>
Address: {{order.anonymised_address}}
<hr>
{% endfor %}
{% endblock content %}
Вместо того, чтобы иметь этот длинный запрос в каждом представлении, можно заменить его на пользовательский менеджер:
models.py:
class AnonymousOrdersManager(models.Manager):
def get_queryset(self):
return super().get_queryset().annotate(anonymised_address=Case(
When(is_anonymized=True, then=None),
When(is_anonymized=False, then='billing_address'),
)).values('is_anonymized', 'anonymised_address')
class Order(models.Model):
is_anonymized = models.BooleanField(default=False)
billing_address = models.ForeignKey(BillingAdress, null=True, blank=True, on_delete=models.CASCADE)
objects = models.Manager()
anonymous_orders = AnonymousOrdersManager()
views.py:
def anonymous_address(request):
orders = Order.anonymous_orders.all()
context = {'orders': orders}
return render(request, 'anonymous_address.html', context)