2012-09-01 41 views
5

Tôi có mối quan hệ M2M giữa hai Mô hình sử dụng mô hình trung gian. Vì mục đích của cuộc thảo luận, chúng ta hãy sử dụng ví dụ từ hướng dẫn:Quan điểm dựa trên lớp cho mối quan hệ M2M với mô hình trung gian

class Person(models.Model): 
    name = models.CharField(max_length=128) 

    def __unicode__(self): 
     return self.name 

class Group(models.Model): 
    name = models.CharField(max_length=128) 
    members = models.ManyToManyField(Person, through='Membership') 

    def __unicode__(self): 
     return self.name 

class Membership(models.Model): 
    person = models.ForeignKey(Person) 
    group = models.ForeignKey(Group) 
    date_joined = models.DateField() 
    invite_reason = models.CharField(max_length=64) 

Tôi muốn tận dụng các quan điểm của Django Class-based, để tránh phải viết quan điểm CRUD xử lý. Tuy nhiên, nếu tôi cố gắng sử dụng createView mặc định, nó không hoạt động:

class GroupCreate(CreateView): 
    model=Group 

này làm cho một hình thức với tất cả các lĩnh vực trên đối tượng Group, và đưa ra một hộp chọn nhiều cho lĩnh vực các thành viên, đó sẽ là chính xác cho một mối quan hệ M2M đơn giản. Tuy nhiên, không có cách nào để chỉ định date_joined hoặc invite_reason và gửi biểu mẫu cung cấp AttributeError sau:

"Không thể đặt giá trị trên ManyToManyField chỉ định mô hình trung gian. Thay vào đó, hãy sử dụng Trình quản lý của thành viên."

Có cách nào gọn gàng để ghi đè lên một phần của CreateView chung hoặc soạn chế độ xem tùy chỉnh của riêng tôi để làm điều này với mixin không? Nó cảm thấy như thế này nên là một phần của khuôn khổ, như giao diện quản trị atomatically xử lý M2M mối quan hệ với trung gian sử dụng inlines.

+0

bản sao có thể có của [django Không thể đặt giá trị trên ManyToManyField chỉ định mô hình trung gian. Sử dụng Trình quản lý thay thế] (http://stackoverflow.com/questions/3091328/django-cannot-set-values-on-a-manytomanyfield-which-specifies-an-intermediary-mo) – juliocesar

Trả lời

6

Bạn phải mở rộng CreateView:

from django.views.generic import CreateView 

class GroupCreate(CreateView): 
    model=Group 

và ghi đè lên form_valid():

from django.views.generic.edit import ModelFormMixin 
from django.views.generic import CreateView 

class GroupCreate(CreateView): 
    model = Group 

    def form_valid(self, form): 
     self.object = form.save(commit=False) 
     for person in form.cleaned_data['members']: 
      membership = Membership() 
      membership.group = self.object 
      membership.person = person 
      membership.save() 
     return super(ModelFormMixin, self).form_valid(form) 

Khi documentation nói, bạn phải tạo mới membership s cho mỗi mối quan hệ giữa groupperson.

tôi thấy form_valid ghi đè ở đây: Using class-based UpdateView on a m-t-m with an intermediary model

+0

Tôi đã không có cơ hội để kiểm tra điều này (và tôi đã đi xuống một con đường khác nhau), nhưng nó trông giống như phương pháp chính xác. Chúc mừng! – Symmetric

+0

Điều này không cung cấp cho bạn bất kỳ cơ hội để nhập 'date_joined' và' invite_reason' ... nó chỉ tiết kiệm cho họ trống ... –

0

Tôi đã gặp phải sự cố tương tự chỉ vài ngày trước. Django có vấn đề để xử lý các mối quan hệ trung gian m2m.

Đây là giải pháp những gì tôi đã tìm thấy hữu ích:

1. Define new CreateView 
class GroupCreateView(CreateView): 
    form_class = GroupCreateForm 
    model = Group 
    template_name = 'forms/group_add.html' 
    success_url = '/thanks' 

Sau đó, thay đổi phương pháp tiết kiệm của hình thức định nghĩa - GroupCreateForm. Lưu có trách nhiệm thực hiện các thay đổi vĩnh viễn cho DB. Tôi đã không thể thực hiện công việc này chỉ qua ORM, vì vậy tôi đã sử dụng SQL thô quá:

1. Define new CreateView 
class GroupCreateView(CreateView): 


class GroupCreateForm(ModelForm): 
    def save(self): 
     # get data from the form 
     data = self.cleaned_data 
     cursor = connection.cursor() 
     # use raw SQL to insert the object (in your case Group) 
     cursor.execute("""INSERT INTO group(group_id, name) 
          VALUES (%s, %s);""" (data['group_id'],data['name'],)) 
     #commit changes to DB 
     transaction.commit_unless_managed() 
     # create m2m relationships (using classical object approach) 
     new_group = get_object_or_404(Group, klient_id = data['group_id']) 
     #for each relationship create new object in m2m entity 
     for el in data['members']: 
      Membership.objects.create(group = new_group, membership = el) 
     # return an object Group, not boolean! 
     return new_group 

Lưu ý: Tôi đã thay đổi mô hình một chút, như bạn thấy (tôi có riêng độc đáo IntegerField cho khóa chính, không sử dụng serial đó là cách nó đã vào get_object_or_404

+0

Câu trả lời này không giúp ích gì nhiều. Bạn có thể hiển thị mô hình của bạn không? Thay đổi pk từ "id" thành "group_id" không có ý nghĩa nhiều. – Timo

0

'để tham khảo, tôi đã không kết thúc bằng một cái nhìn dựa trên lớp, thay vào đó tôi đã làm một cái gì đó như thế này:.

def group_create(request): 
    group_form = GroupForm(request.POST or None) 
    if request.POST and group_form.is_valid(): 
     group = group_form.save(commit=False) 
     membership_formset = MembershipFormSet(request.POST, instance=group) 
     if membership_formset.is_valid(): 
      group.save() 
      membership_formset.save() 
      return redirect('success_page.html') 
    else: 
     # Instantiate formset with POST data if this was a POST with an invalid from, 
     # or with no bound data (use existing) if this is a GET request for the edit page. 
     membership_formset = MembershipFormSet(request.POST or None, instance=Group()) 

    return render_to_response(
     'group_create.html', 
     { 
      'group_form': recipe_form, 
      'membership_formset': membership_formset, 
     }, 
     context_instance=RequestContext(request), 
    ) 

Đây có thể là điểm khởi đầu cho việc triển khai dựa trên Lớp học, nhưng đơn giản là nó không phải là w chỉnh sửa thời gian của tôi để cố gắng đánh thức điều này vào mô hình dựa trên lớp học.

0

Chỉ cần một bình luận, khi sử dụng CBV bạn cần phải lưu mẫu với cam kết = True, vì vậy nhóm được tạo ra và một id được đưa ra có thể được sử dụng để tạo thành viên. Nếu không, với commit = False, đối tượng nhóm không có id nào và lỗi được tăng lên.

+0

Bất kỳ cơ hội nhận được thêm thông tin về điều này ...? Sau câu trả lời được chấp nhận, cách duy nhất tôi có thể lấy được mã để lưu 'Membership' là sử dụng' commit = False', nếu không nó sẽ ném 'AttributeError' được hiển thị trong câu hỏi. Trong thực tế, ngay cả khi tôi cố gắng sử dụng 'form.save()' sau khi lưu 'Membership', tôi vẫn nhận được' AttributeError' đó. Tuy nhiên, như bạn nói, không có 'id' cho' nhóm', vì vậy 'Membership' không được lưu đúng cách ... –

+0

Điều tôi muốn nói là giải pháp được đề xuất sẽ không hoạt động, bởi vì khi bạn kiểm tra , cá thể nhóm chưa được lưu vào DB, vì vậy nó không có id. Những gì bạn phải làm là tạo một nhóm mới chỉ với một tên và lưu nó (commit = True). Bây giờ nó có một id, và bạn có thể tạo các đối tượng thành viên mới. Về lỗi, tôi đã không thử mã vì vậy tôi không biết lý do chính xác. Có thể, hình thức tự tạo ra bao gồm một lĩnh vực bắt buộc với các thành viên ... Bạn có biết thêm chi tiết về điều này không? – kiril

+0

Tạo một nhóm mới với id mới có vẻ là tốt, nhưng làm thế nào tôi có thể nhập date_joined và invite_reason như Zad-Man đã nói? Có ai đó là một giải pháp hoàn chỉnh với chế độ xem, mô hình. Tôi nghĩ rằng nó có thể giúp đỡ để xem cách ứng dụng "admin" giải quyết nó. – Timo

2
class GroupCreate(CreateView): 
    model = Group 

    def form_valid(self, form): 
     self.object = form.save(commit=False) 

     ### delete current mappings 
     Membership.objects.filter(group=self.object).delete() 

     ### find or create (find if using soft delete) 
     for member in form.cleaned_data['members']: 
      x, created = Membership.objects.get_or_create(group=self.object, person=member) 
      x.group = self.object 
      x.person = member 
      #x.alive = True # if using soft delete 
      x.save() 
     return super(ModelFormMixin, self).form_valid(form) 
Các vấn đề liên quan