Django Admin: как «предварительно заполнить» встроенное поле внешнего ключа на основе родительской модели - PullRequest
1 голос
/ 31 января 2012

Некоторые комментарии к этому вопросу указывают на то, что существует более простое и актуальное решение. (Я не нашел)

(комментарии типа: «Да, это решено. Просто передайте связанный экземпляр в параметре, и он предварительно заполнит его ...», заставляет меня думать, что теперь это легко, но все же я не могу заставить его работать)

Дело в том, что я хочу отправить уведомление некоторым пользователям, которые вовлечены в данное событие и выбраны администратором. Сотрудник находится на странице события, выбирает событие и сохраняет его, после чего встроенные поля fk должны быть предварительно заполнены соответствующими именами пользователей. (и ждет, пока сотрудник проверит их или снимет галочку для получения уведомления по электронной почте)

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

Есть идеи?

class When(models.Model):
    ...
    Date = models.DateTimeField(unique=True)
    Nameofevent = models.ForeignKey(Event, to_field='Name') #the Event model gets then username from models.ManyToManyField(User, through='Role')
    ...
    def getteam(self):
        teamlist = self.Nameofevent.Roleinevent.all() # gathering users which are involved
        return teamlist
    getteamresult = property(getteam)

class Notice(models.Model): #this will go to the inline
    ...
    Who = models.ForeignKey(User, blank=True)
    To_notice_or_not_to_notice = models.BooleanField(default=False)
    ...

class NoticeInline(admin.TabularInline):
    model = Notice
    extra = 9

class WhenAdmin(admin.ModelAdmin):
    list_display = ('Date', ...)
    readonly_fields = ('getteamresult', ) #this is to make clear i have access to what i want and i can display it. prints out as [<User: someone>, <User: someoneelse>]
    inlines = [NoticeInline] #but i want the User objects to prepopulate foreign field here in inline model
admin.site.register(When,WhenAdmin)

1 Ответ

1 голос
/ 31 января 2012

Я не верю, что inline-это способ для чего-то подобного. Если сотруднику необходимо предложить отправить электронное письмо пользователям, участвующим в мероприятии, и ему необходимо контролировать, кто из них действительно уведомлен, то вам следует использовать промежуточное представление.

Во-первых, вам нужна форма, которая позволит вам выбирать пользователей, которые относятся к событию. Изначально мы устанавливаем поле users только для всех пользователей, но в методе __init__ формы мы возьмем kwarg «event» и отфильтруем поле на основе этого.

class UserNotifyForm(forms.Form):
    users = forms.ModelMultipleChoiceField(queryset=User.objects.all(), widget=forms.CheckboxSelectMultiple())

    def __init__(self, *args, **kwargs):
        event = kwargs.pop('event')
        super(UserNotifyForm, self).__init__(*args, **kwargs)
        if event:
            self.fields['users'].queryset = event.users.all()

Во-вторых, вы создаете представление на ModelAdmin, которое будет вести себя так же, как обычное представление формы:

def notify_users_view(self, request, object_id):
    event = get_object_or_404(Event, id=object_id)
    if len(request.POST):
        form = UserNotifyForm(request.POST, event=event)
        if form.is_valid():
            users = form.cleaned_data.get('users')
            # send email
            return HttpResponseRedirect(reverse('admin:yourapp_event_changelist'))
    else:
        form = UserNotifyForm(event=event)

    return render_to_response('path/to/form/template.html', {
        'event': event,
        'form': form,
    }, context_instance=RequestContext(request))

Вам, конечно, нужно будет создать шаблон для этого, но это достаточно просто. Форма уже настроена для отображения списка флажков, по одному для каждого пользователя, поэтому у вас есть вся необходимая информация.

В-третьих, привяжите это представление к URL ModelAdmin:

def get_urls(self):
    urls = super(MyModelAdmin, self).get_urls()

    info = (self.model._meta.app_label, self.model._meta.module_name)

    my_urls = patterns('',
        (r'^(?P<object_id>\d+)/notify/$', self.notify_users_view, name='%s_%s_notify' % info)
    )
    return my_urls + urls

В-четвертых, переопределите change_view, чтобы перенаправить на это представление после сохранения:

def change_view(self, request, object_id, extra_context=None):
    response = super(MyModelAdmin, self).change_view(request, object_id, extra_context=extra_context)
    if len(request.POST):
        info = (self.model._meta.app_label, self.model._meta.module_name)
        response['Location'] = reverse('admin:%s_%s_notify', args=(object_id,))
    # Note: this will effectively negate the 'Save and Continue' and
    # 'Save and Add Another' buttons. You can conditionally check
    # for these based on the keys they add to request.POST and branch
    # accordingly to some other behavior you desire.
    return response
...