m2m django implementation after following a tutorial

时间:2019-04-17 01:56:48

标签: python django python-3.x m2m

Scenario: I have some boxes (containers) I have some objects (samples) a sample can be split over many boxes, a box can contain many samples.

I want to be able to assign a sample to a box and remove a sample from a box.

I followed these tutorials 57-59, assigning friends to users, and got it working.

So I now try to adapt the code, so I need to change users to boxes/containers and friends to samples. Sounds simple enough. But I'm inexperienced with the quirks of Django and where the request.user is I can't seem to get the correct syntax. So here comes the code, first the code working from the tutorial, then my attempt at refactoring it.

I have 2 other tables/models Containers and Sample which the ContainerContent model fits inbetween.

# models.py (tutorial)
class Friend(models.Model):
    users = models.ManyToManyField(User)
    current_user = models.ForeignKey(User, related_name='owner', null=True, on_delete = models.PROTECT)
    # container_id = models.ForeignKey(Container, null=True, on_delete = models.PROTECT)

    @classmethod
    def make_friend(cls, current_user, new_friend):
        friend, created = cls.objects.get_or_create(
            current_user=current_user
        )
        friend.users.add(new_friend)

    @classmethod
    def lose_friend(cls, current_user, new_friend):
        friend, created = cls.objects.get_or_create(
            current_user=current_user
        )
        friend.users.remove(new_friend)


# views.py
def change_friends(request, operation, pk):
    friend = User.objects.get(pk=pk)
    if operation == 'add':
        Friend.make_friend(request.user, friend)
    elif operation == 'remove':
        Friend.lose_friend(request.user, friend)

    return redirect('depot:allcontainer')


#urls.py
url(r'^container/(?P<operation>.*)/(?P<pk>\d+)/$', views.change_friends, name='change_friends'),


#html
...
        <tbody>
          {% for user in users %}
          <tr>
            {% if user not in friends %}
            <!-- we will want to add an if stmt list if not in unassigned - need to think how to do this -->
            <td>{{ container.container_id }}</td>
            <td>{{ user.username }}</td>
            <td>  <a href="{% url 'depot:change_friends' operation='add' pk=user.pk %}"  class="badge badge-primary" role="button">
              <!-- container=container.container_id -->
              <!-- container=container.container_id -->
              <!-- <button type="button" class="btn btn-success">add</button> -->
              >>
            </a></td>
            {% endif %}
          </tr>
          {% endfor %}
        </tbody>
...

...
      <tbody>
      <tr>
          {% for friend in friends %}
          <td><a href="{% url 'depot:change_friends'  operation='remove' pk=friend.pk %}" class="badge badge-primary" role="button">
            <<
          </a></td>
          <td>{{ friend.username }}</td>
        </tr>
        {% endfor %}
      </tbody>
...

Below is my Attempt:

# models.py
class ContainerContents(models.Model):
    sample = models.ManyToManyField('Sample')
    current_container = models.ForeignKey(Container, null=True, on_delete = models.PROTECT)

        @classmethod
        def add_to_container(cls, current_container, new_sample):
            sample, created = cls.objects.get_or_create(
                current_container=current_container
            )
            sample.add(new_sample)

        @classmethod
        def remove_from_container(cls, current_container, new_sample):
            sample, created = cls.objects.get_or_create(
                current_container=current_container
            )
            sample.remove(new_sample)

# views.py - this one is causing me issues, the request.____
def change_container(request, operation, pk, fk='', sample_id=''):
    container = Container.objects.get(pk=pk)
    sample = Sample.objects.get(pk=fk)
    # sample = Container.objects.get(container.sample_id=sample_id)
    if operation == 'add':
        ContainerContents.add_to_container(request.container, container)
    elif operation == 'remove':
        ContainerContents.remove_from_container(request.container, container)

    return redirect('depot:allcontainer')

# urls.py
url(r'^change_container/(?P<operation>.*)/(?P<pk>\d+)/sample/(?P<fk>\d+)$', views.change_container, name='change_container'),

I suspect I need to pass the container id here otherwise there will not be any distinction between the containers.

# html
    <tbody>
      {% for unassigned in container_contents %}
      <tr>
        <!-- { if user not in friends } -->
        <!-- we will want to add an if stmt list if not in unassigned - need to think how to do this -->
        <td>{{ unassigned.area_easting }}.
          {{ unassigned.area_northing }}.
          {{ unassigned.context_number }}.
          {{ unassigned.sample_number }}</td>
          <td>{{ unassigned.sample_id }}</td>
          <td></td>
          <td>  <a href="{ url 'depot:change_friends' operation='add' pk=user.pk }"  class="badge badge-primary" role="button">
            <!-- container=container.container_id -->
            <!-- container=container.container_id -->
            <!-- <button type="button" class="btn btn-success">add</button> -->
            >>
          </a></td>
          <!-- { endif } -->
        </tr>
        {% endfor %}
      </tbody>
...
...

    <tbody>
      <tr>
        {% for contents in container_contents %}
        <td><a href="{% url 'depot:change_container'  operation='remove' pk=container.container_id fk=contents.sample_id  %}" class="badge badge-primary" role="button">
          <!-- <button type="button" class="btn btn-default">remove</button> -->
          <<
        </a></td>
        <td>{{ contents.sample_id }}</td>
        <td>{{ contents.area_easting }}.
          {{ contents.area_northing }}.
          {{ contents.context_number }}.
          {{ contents.sample_number }}</td>
        </tr>
        {% endfor %}
      </tbody>
...       

--- Update ---

I should have included the view that generates the page, not the users/friends is still contained in it and will be removed once I get it working.

def detailcontainer(request, container_id):
    container = get_object_or_404(Container, pk=container_id)
    samples = container.samples.all()
    # allsamples = container.samples.exclude(sample_id=samples)
    allsamples = container.samples.all()

    users = User.objects.exclude(id=request.user.id).order_by('-id')
    friend = Friend.objects.get(current_user=request.user)
    friends = friend.users.all().order_by('-id')

    container_contents = container.samples.all()
    # container_contents = Container.objects.get(current_container=samples)

    return render(request, 'container/detailcontainer.html',
    {'container':container,
    'samples':samples,
    'allsamples': allsamples,

    'users': users,
    'friends': friends,

    'container_contents': container_contents,

    })

2 个答案:

答案 0 :(得分:1)

这应该引起问题,因为request没有任何名为container的属性。在您的教程示例中,它使用request.user已登录的用户,因为django(通过中间件)将登录的用户实例分配给了request

由于sample视图方法中已经有containerchange_container个对象,因此您可以尝试以下操作:

if operation == 'add':
    ContainerContents.add_to_container(container, sample)
elif operation == 'remove':
    ContainerContents.remove_from_container(container, sample)

更新

缺少一件事,您还需要在add_to_containerremove_from_container方法内部进行更改:

    @classmethod
    def add_to_container(cls, current_container, new_sample):
        container, created = cls.objects.get_or_create(
            current_container=current_container
        )
        container.sample.add(new_sample)

    @classmethod
    def remove_from_container(cls, current_container, new_sample):
        container, created = cls.objects.get_or_create(
            current_container=current_container
        )
        container.sample.remove(new_sample)

因为示例是CurrentContainerSample模型之间的ManyToMany Field连接。

更新2

@classmethod
def remove_from_container(cls, current_container, new_sample):
     from app_name.models import ContainerSample

     c_sample = ContainerSample.objects.get(container=current_container, sample=new_sample)
     c_sample.delete()

答案 1 :(得分:1)

您没有在获取的对象中引用您的m2m字段。您需要按如下所示处理sample字段:

models.py:

@classmethod
def add_to_container(cls, current_container, new_sample):
    containerContents, created = cls.objects.get_or_create(
        current_container=current_container
    )
    containerContents.sample.add(new_sample)

@classmethod
def remove_from_container(cls, current_container, new_sample):
    containerContents, created = cls.objects.get_or_create(
        current_container=current_container
    )
    containerContents.sample.remove(new_sample)

并为模型方法设置适当的变量:

views.py

def change_container(request, operation, pk, fk='', sample_id=''):
    container = Container.objects.get(pk=pk)
    sample = Sample.objects.get(pk=fk)
    # sample = Container.objects.get(container.sample_id=sample_id)
    if operation == 'add':
        ContainerContents.add_to_container(container, sample)
    elif operation == 'remove':
        ContainerContents.remove_from_container(container, sample)

    return redirect('depot:allcontainer')