Kirill
Kirill

Reputation: 71

How to access ManyRelatedManager object attribute using mptt

What I got:

class Category(MPTTModel):
    name = models.CharField(max_length=50, unique=True)
    slug = models.SlugField(unique=True)
    full_slug = models.CharField(max_length=256, null=True, blank=True)
    parent = TreeForeignKey('self', null=True, blank=True, related_name='children', db_index=True)

class Work(models.Model):
    title = models.CharField(max_length=128, unique=True)
    slug = models.SlugField(max_length=128, unique=True)    
    category = TreeManyToManyField(Category, verbose_name='parent category', related_name='works')
    url = models.CharField(max_length=256, null=True, blank=True)

    def save(self, *args, **kwargs):
        if self.category:
            self.url = '%s/%s' % (self.category.full_slug, self.slug)
        else:
            self.url = '%s' % self.slug
        super(Work, self).save(*args, **kwargs)

But I get an error "'ManyRelatedManager' object has no attribute 'full_slug'". Actually I need only one category, so I tried self.url = '%s/%s' % (self.category.all()[0].full_slug, self.slug), but I get the error anyway.

So, is there a way to solve this? The only way I get it working is with category=TreeForeignKey, but I need a TreeManyToManyField

Traceback:

Environment:


Request Method: POST
Request URL: http://127.0.0.1:8000/admin/website/work/1/change/

Django Version: 1.9.5
Python Version: 3.5.1
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'mptt',
 'django_mptt_admin',
 'website']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']



Traceback:

File "E:\web\env\lib\site-packages\django\core\handlers\base.py" in get_response
  149.                     response = self.process_exception_by_middleware(e, request)

File "E:\web\env\lib\site-packages\django\core\handlers\base.py" in get_response
  147.                     response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "E:\web\env\lib\site-packages\django\contrib\admin\options.py" in wrapper
  541.                 return self.admin_site.admin_view(view)(*args, **kwargs)

File "E:\web\env\lib\site-packages\django\utils\decorators.py" in _wrapped_view
  149.                     response = view_func(request, *args, **kwargs)

File "E:\web\env\lib\site-packages\django\views\decorators\cache.py" in _wrapped_view_func
  57.         response = view_func(request, *args, **kwargs)

File "E:\web\env\lib\site-packages\django\contrib\admin\sites.py" in inner
  244.             return view(request, *args, **kwargs)

File "E:\web\env\lib\site-packages\django\contrib\admin\options.py" in change_view
  1440.         return self.changeform_view(request, object_id, form_url, extra_context)

File "E:\web\env\lib\site-packages\django\utils\decorators.py" in _wrapper
  67.             return bound_func(*args, **kwargs)

File "E:\web\env\lib\site-packages\django\utils\decorators.py" in _wrapped_view
  149.                     response = view_func(request, *args, **kwargs)

File "E:\web\env\lib\site-packages\django\utils\decorators.py" in bound_func
  63.                 return func.__get__(self, type(self))(*args2, **kwargs2)

File "C:\Python35\Lib\contextlib.py" in inner
  30.                 return func(*args, **kwds)

File "E:\web\env\lib\site-packages\django\contrib\admin\options.py" in changeform_view
  1378.                 self.save_model(request, new_object, form, not add)

File "E:\web\env\lib\site-packages\django\contrib\admin\options.py" in save_model
  991.         obj.save()

File "E:\web\website\models.py" in save
  63.             self.url = '%s/%s' % (self.category.full_slug, self.slug)

Exception Type: AttributeError at /admin/website/work/1/change/
Exception Value: 'ManyRelatedManager' object has no attribute 'full_slug'

Upvotes: 0

Views: 1280

Answers (1)

lukeaus
lukeaus

Reputation: 12265

In simple terms you are getting this error because if you look at your code

    category = TreeManyToManyField(Category, verbose_name='parent category', related_name='works')

Its a many-to-many-field So here:

    def save(self, *args, **kwargs):
        if self.category:
            self.url = '%s/%s' % (self.category.full_slug, self.slug)

you are saying give me category.full_slug

And django is politely telling you that there can be multiple categories for and Work instance.

So its up to you to figure out which self.category instance you want.

e.g.

 self.category.first().full_slug

Read more here: https://docs.djangoproject.com/en/1.9/ref/models/relations/

Of course self.category.first() may not exist if you don't have any Category instances that are related to the Work instance you are trying to save.

On a highly related note, you need to save the Work instance first before associating any m2m models to your Work instance.

Upvotes: 0

Related Questions