Repository: digitalfabrik/integreat-cms

Issue:
Language tree node form not working
### Describe the Bug
<!-- A clear and concise description of what the bug is. -->
Since commit 928dec60b79ba47366099ca4227b2abe0acb2de7, the language node form is not working anymore.

### Steps to Reproduce

1. Go to a region
2. Open language tree
3. Open language tree node
4. Click on save

### Expected Behavior
<!-- A clear and concise description of what you expected to happen. -->
The language tree node should be saved

### Actual Behavior
<!-- A clear and concise description of what actually happened. -->
the following error appears:
```
MultipleObjectsReturned at /augsburg/language-tree/4/edit

get() returned more than one LanguageTreeNode -- it returned 3!
```
### Additional Information
<!-- Add any other context (e.g. logs, screenshots, etc.) about the problem here. -->
<details>
  <summary>Debug Stacktrace</summary>


Environment:


Request Method: POST
Request URL: http://localhost:8000/augsburg/language-tree/4/edit

Django Version: 2.2.16
Python Version: 3.7.9
Installed Applications:
['cms.apps.CmsConfig',
 'gvz_api.apps.GvzApiConfig',
 'django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.messages',
 'django.contrib.sessions',
 'django.contrib.sitemaps',
 'django.contrib.staticfiles',
 'compressor',
 'compressor_toolkit',
 'corsheaders',
 'widget_tweaks',
 'easy_thumbnails',
 'filer',
 'mptt',
 'rules.apps.AutodiscoverRulesConfig']
Installed Middleware:
['corsheaders.middleware.CorsMiddleware',
 'django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.locale.LocaleMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']



Traceback:

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/core/handlers/exception.py" in inner
  34.             response = get_response(request)

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/core/handlers/base.py" in _get_response
  115.                 response = self.process_exception_by_middleware(e, request)

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/core/handlers/base.py" in _get_response
  113.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/views/generic/base.py" in view
  71.             return self.dispatch(request, *args, **kwargs)

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/utils/decorators.py" in _wrapper
  45.         return bound_method(*args, **kwargs)

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/contrib/auth/decorators.py" in _wrapped_view
  21.                 return view_func(request, *args, **kwargs)

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/utils/decorators.py" in _wrapper
  45.         return bound_method(*args, **kwargs)

File "/home/timo/job/integreat/django/src/cms/decorators.py" in wrap
  53.             return function(request, *args, **kwargs)

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/contrib/auth/mixins.py" in dispatch
  85.         return super().dispatch(request, *args, **kwargs)

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/views/generic/base.py" in dispatch
  97.         return handler(request, *args, **kwargs)

File "/home/timo/job/integreat/django/src/cms/views/language_tree/language_tree_node_view.py" in post
  58.         if not language_tree_node_form.is_valid():

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/forms/forms.py" in is_valid
  185.         return self.is_bound and not self.errors

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/forms/forms.py" in errors
  180.             self.full_clean()

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/forms/forms.py" in full_clean
  381.         self._clean_fields()

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/forms/forms.py" in _clean_fields
  399.                     value = field.clean(value)

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/forms/fields.py" in clean
  148.         value = self.to_python(value)

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/forms/models.py" in to_python
  1248.             value = self.queryset.get(**{key: value})

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/db/models/query.py" in get
  412.             (self.model._meta.object_name, num)

Exception Type: MultipleObjectsReturned at /augsburg/language-tree/4/edit
Exception Value: get() returned more than one LanguageTreeNode -- it returned 3!
</details>

I assume that this is a bug in the mptt library - TreeQuerySets don't seem to work properly with the `difference()`-method...
Language tree node form not working
### Describe the Bug
<!-- A clear and concise description of what the bug is. -->
Since commit 928dec60b79ba47366099ca4227b2abe0acb2de7, the language node form is not working anymore.

### Steps to Reproduce

1. Go to a region
2. Open language tree
3. Open language tree node
4. Click on save

### Expected Behavior
<!-- A clear and concise description of what you expected to happen. -->
The language tree node should be saved

### Actual Behavior
<!-- A clear and concise description of what actually happened. -->
the following error appears:
```
MultipleObjectsReturned at /augsburg/language-tree/4/edit

get() returned more than one LanguageTreeNode -- it returned 3!
```
### Additional Information
<!-- Add any other context (e.g. logs, screenshots, etc.) about the problem here. -->
<details>
  <summary>Debug Stacktrace</summary>


Environment:


Request Method: POST
Request URL: http://localhost:8000/augsburg/language-tree/4/edit

Django Version: 2.2.16
Python Version: 3.7.9
Installed Applications:
['cms.apps.CmsConfig',
 'gvz_api.apps.GvzApiConfig',
 'django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.messages',
 'django.contrib.sessions',
 'django.contrib.sitemaps',
 'django.contrib.staticfiles',
 'compressor',
 'compressor_toolkit',
 'corsheaders',
 'widget_tweaks',
 'easy_thumbnails',
 'filer',
 'mptt',
 'rules.apps.AutodiscoverRulesConfig']
Installed Middleware:
['corsheaders.middleware.CorsMiddleware',
 'django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.locale.LocaleMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']



Traceback:

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/core/handlers/exception.py" in inner
  34.             response = get_response(request)

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/core/handlers/base.py" in _get_response
  115.                 response = self.process_exception_by_middleware(e, request)

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/core/handlers/base.py" in _get_response
  113.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/views/generic/base.py" in view
  71.             return self.dispatch(request, *args, **kwargs)

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/utils/decorators.py" in _wrapper
  45.         return bound_method(*args, **kwargs)

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/contrib/auth/decorators.py" in _wrapped_view
  21.                 return view_func(request, *args, **kwargs)

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/utils/decorators.py" in _wrapper
  45.         return bound_method(*args, **kwargs)

File "/home/timo/job/integreat/django/src/cms/decorators.py" in wrap
  53.             return function(request, *args, **kwargs)

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/contrib/auth/mixins.py" in dispatch
  85.         return super().dispatch(request, *args, **kwargs)

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/views/generic/base.py" in dispatch
  97.         return handler(request, *args, **kwargs)

File "/home/timo/job/integreat/django/src/cms/views/language_tree/language_tree_node_view.py" in post
  58.         if not language_tree_node_form.is_valid():

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/forms/forms.py" in is_valid
  185.         return self.is_bound and not self.errors

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/forms/forms.py" in errors
  180.             self.full_clean()

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/forms/forms.py" in full_clean
  381.         self._clean_fields()

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/forms/forms.py" in _clean_fields
  399.                     value = field.clean(value)

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/forms/fields.py" in clean
  148.         value = self.to_python(value)

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/forms/models.py" in to_python
  1248.             value = self.queryset.get(**{key: value})

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/db/models/query.py" in get
  412.             (self.model._meta.object_name, num)

Exception Type: MultipleObjectsReturned at /augsburg/language-tree/4/edit
Exception Value: get() returned more than one LanguageTreeNode -- it returned 3!
</details>

I assume that this is a bug in the mptt library - TreeQuerySets don't seem to work properly with the `difference()`-method...


Discussion:

Resolving patch:
diff --git a/src/cms/forms/language_tree/language_tree_node_form.py b/src/cms/forms/language_tree/language_tree_node_form.py
--- a/src/cms/forms/language_tree/language_tree_node_form.py
+++ b/src/cms/forms/language_tree/language_tree_node_form.py
@@ -48,7 +48,7 @@ def __init__(self, *args, **kwargs):
 
         if self.instance.id:
             children = self.instance.get_descendants(include_self=True)
-            parent_queryset = parent_queryset.difference(children)
+            parent_queryset = parent_queryset.exclude(id__in=children)
         else:
             self.instance.region = region
 


Test patch: