我有一个带有用户外键的预订模型。在管理员中,预订内嵌在用户更改页面中。
我希望在预订前不到24小时内阻止某些预订被删除(来自内联)并且记录的用户不在SuperStaff组中。
所以我定义了类似的BookingInline:
class BookingInline(admin.TabularInline):
model = Booking
extra = 0
fk_name = 'bookedFor'
def has_delete_permission(self, request, obj=None):
if not request.user.profile.isSuperStaff() and obj.is24hoursFromNow():
return True
return False
此代码已到达,但我获得了一个用户实例,而不是预订一个(当然也是错误),因此无法决定每个内联预订是否可以删除。 在这种情况下,has_delete_permission()方法是不是应该获取内联对象实例? django docs中没有任何内容......
我知道代码已经到达,因为我只使用了用户的条件来检查它,它实际上隐藏了适当用户的删除框。
我也尝试过其他方式,通过Formset和clean()方法,但它没有请求参数,所以我得到了所需的实例,但没有登录的用户。
我已经搜索了几个小时的解决方案,但似乎唯一的方法是从内联链接到Booking对象的完整更改页面,并在用户定期尝试时检查权限删除预订。
任何想法如何以优雅的方式完成将不胜感激。
答案 0 :(得分:3)
我今天遇到了完全相同的问题,我想我找到了一种可以接受的解决方法。这是我做的:
仅当特定字段具有特定值时,我才必须使内联删除。具体来说,当我处理通用任务和分配时,只有不可接受的任务必须是可删除的。在模型方面:
class Task(models.Model):
STATUS_CHOICES = (
('PND', 'Pending'),
('ACC', 'Accepted'),
)
status = models.CharField( ----> If this != 'PND', inline instance
max_length=3, should not be deletable
choices=STATUS_CHOICES,
default=STATUS_CHOICES[0][0])
由于我无法在has_delete_permission
类中使用admin.TabularInline
,因为它引用整个字段集(即所有内联)而不是单行,我经历了模板覆盖:
tabular.html:44-62(原创)
[...]
{% for fieldset in inline_admin_form %}
{% for line in fieldset %}
{% for field in line %}
{% if not field.field.is_hidden %}
<td{% if field.field.name %} class="field-{{ field.field.name }}"{% endif %}>
{% if field.is_readonly %}
<p>{{ field.contents }}</p>
{% else %}
{{ field.field.errors.as_ul }}
{{ field.field }}
{% endif %}
</td>
{% endif %}
{% endfor %}
{% endfor %}
{% endfor %}
{% if inline_admin_formset.formset.can_delete %}
<td class="delete">{% if inline_admin_form.original %}{{ inline_admin_form.deletion_field.field }}{% endif %}</td>
{% endif %}
[...]
tabular.html(已覆盖)
[...]
{% for fieldset in inline_admin_form %}
{% for line in fieldset %}
{% for field in line %}
{% if not field.field.is_hidden %}
<td{% if field.field.name %} class="field-{{ field.field.name }}"{% endif %}>
{% if field.is_readonly %}
<p>{{ field.contents }}</p>
{% else %}
{% include "admin/includes/field.html" with is_tabular=True %}
{% endif %}
</td>
{% endif %}
{% endfor %}
{% endfor %}
<!-- Custom deletion logic, only available for non-accepted objects -->
{% for line in fieldset %}
{% for field in line %}
{% if field.field.name == "status" %}
{% if field.field.value == "PND" %}
<td class="delete">{% if inline_admin_form.original %}{{ inline_admin_form.deletion_field.field }}{% endif %}</td>
{% else %}
<td class="delete"><input type="checkbox" disabled="disabled">
<img src="/static/admin/img/icon_alert.gif" data-toggle="tooltip" class="title-starter"
data-original-title="Can't remove accepted tasks" />
</td>
{% endif %}
{% endif %}
{% endfor %}
{% endfor %}
{% endfor %}
<!-- Classic deletion, removed
{% if inline_admin_formset.formset.can_delete %}
<td class="delete">{% if inline_admin_form.original %}{{ inline_admin_form.deletion_field.field }}{% endif %}</td>
{% endif %}
-->
[...]
结果(非标准图形,因为我正在使用django-admin-bootstrap):
严格地谈论“优雅”,我必须两次通过线条的字段以使其工作,但我没有找到任何更好的方式,如直接阅读该字段的价值。我没有像{{ line.fields.0.status }}
或{{ line.fields.status }}
这样的工作。如果有人能指出直接语法,我很乐意更新我的解决方案。
无论如何,既然它仍然有效并且 那么那么糟糕,我会很好地使用这种方法,直到明显更好。
答案 1 :(得分:1)
您可以使用formset的clean()方法检查条件。
class BookingFormSet(forms.BaseInlineFormSet):
def clean(self):
super().clean()
has_errors = False
for form in self.deleted_forms:
if form.instance.is24hoursFromNow():
form._errors[NON_FIELD_ERRORS] = self.error_class(['Not allowed to delete'])
has_errors = True
if has_errors:
raise forms.ValidationError('Please correct the errors below')
class BookingInline(admin.TabularInline):
model = Booking
formset = BookingFormSet
请注意,这里没有请求对象,因此无法检查isSuperStaff()