Merge pull request #4140 from ostcar/autoupdate_and_delete

Autoupdate on element deletion
This commit is contained in:
Norman Jäckel 2019-01-19 15:50:47 +01:00 committed by GitHub
commit 6aeedf39fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 428 additions and 84 deletions

View File

@ -0,0 +1,33 @@
# Generated by Django 2.1.5 on 2019-01-19 13:25
from django.conf import settings
from django.db import migrations, models
import openslides.utils.models
class Migration(migrations.Migration):
dependencies = [("agenda", "0005_auto_20180815_1109")]
operations = [
migrations.AlterField(
model_name="item",
name="parent",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=openslides.utils.models.SET_NULL_AND_AUTOUPDATE,
related_name="children",
to="agenda.Item",
),
),
migrations.AlterField(
model_name="speaker",
name="user",
field=models.ForeignKey(
on_delete=openslides.utils.models.CASCADE_AND_AUTOUODATE,
to=settings.AUTH_USER_MODEL,
),
),
]

View File

@ -15,6 +15,7 @@ from openslides.utils.exceptions import OpenSlidesError
from openslides.utils.models import RESTModelMixin
from openslides.utils.utils import to_roman
from ..utils.models import CASCADE_AND_AUTOUODATE, SET_NULL_AND_AUTOUPDATE
from .access_permissions import ItemAccessPermissions
@ -233,7 +234,7 @@ class Item(RESTModelMixin, models.Model):
parent = models.ForeignKey(
"self",
on_delete=models.SET_NULL,
on_delete=SET_NULL_AND_AUTOUPDATE,
null=True,
blank=True,
related_name="children",
@ -374,7 +375,7 @@ class Speaker(RESTModelMixin, models.Model):
objects = SpeakerManager()
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=CASCADE_AND_AUTOUODATE)
"""
ForeinKey to the user who speaks.
"""

View File

@ -0,0 +1,31 @@
# Generated by Django 2.1.5 on 2019-01-19 13:25
from django.conf import settings
from django.db import migrations, models
import openslides.utils.models
class Migration(migrations.Migration):
dependencies = [("assignments", "0005_auto_20180822_1042")]
operations = [
migrations.AlterField(
model_name="assignmentoption",
name="candidate",
field=models.ForeignKey(
null=True,
on_delete=openslides.utils.models.SET_NULL_AND_AUTOUPDATE,
to=settings.AUTH_USER_MODEL,
),
),
migrations.AlterField(
model_name="assignmentrelateduser",
name="user",
field=models.ForeignKey(
on_delete=openslides.utils.models.CASCADE_AND_AUTOUODATE,
to=settings.AUTH_USER_MODEL,
),
),
]

View File

@ -21,6 +21,7 @@ from openslides.utils.autoupdate import inform_changed_data
from openslides.utils.exceptions import OpenSlidesError
from openslides.utils.models import RESTModelMixin
from ..utils.models import CASCADE_AND_AUTOUODATE, SET_NULL_AND_AUTOUPDATE
from .access_permissions import AssignmentAccessPermissions
@ -36,7 +37,7 @@ class AssignmentRelatedUser(RESTModelMixin, models.Model):
ForeinKey to the assignment.
"""
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=CASCADE_AND_AUTOUODATE)
"""
ForeinKey to the user who is related to the assignment.
"""
@ -366,7 +367,9 @@ class AssignmentOption(RESTModelMixin, BaseOption):
poll = models.ForeignKey(
"AssignmentPoll", on_delete=models.CASCADE, related_name="options"
)
candidate = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
candidate = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=SET_NULL_AND_AUTOUPDATE, null=True
)
weight = models.IntegerField(default=0)
vote_class = AssignmentVote

View File

@ -5,14 +5,10 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0010_auto_20190118_1908'),
]
dependencies = [("core", "0010_auto_20190118_1908")]
operations = [
migrations.AlterField(
model_name='history',
name='now',
field=models.DateTimeField(),
),
model_name="history", name="now", field=models.DateTimeField()
)
]

View File

@ -0,0 +1,41 @@
# Generated by Django 2.1.5 on 2019-01-19 13:25
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
import openslides.utils.models
class Migration(migrations.Migration):
dependencies = [("core", "0011_auto_20190119_0958")]
operations = [
migrations.AlterField(
model_name="chatmessage",
name="user",
field=models.ForeignKey(
on_delete=openslides.utils.models.CASCADE_AND_AUTOUODATE,
to=settings.AUTH_USER_MODEL,
),
),
migrations.AlterField(
model_name="history",
name="user",
field=models.ForeignKey(
null=True,
on_delete=openslides.utils.models.SET_NULL_AND_AUTOUPDATE,
to=settings.AUTH_USER_MODEL,
),
),
migrations.AlterField(
model_name="projectiondefault",
name="projector",
field=models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
related_name="projectiondefaults",
to="core.Projector",
),
),
]

View File

@ -6,7 +6,11 @@ from jsonfield import JSONField
from ..utils.autoupdate import Element
from ..utils.cache import element_cache, get_element_id
from ..utils.models import RESTModelMixin
from ..utils.models import (
CASCADE_AND_AUTOUODATE,
SET_NULL_AND_AUTOUPDATE,
RESTModelMixin,
)
from .access_permissions import (
ChatMessageAccessPermissions,
ConfigAccessPermissions,
@ -108,7 +112,7 @@ class ProjectionDefault(RESTModelMixin, models.Model):
display_name = models.CharField(max_length=256)
projector = models.ForeignKey(
Projector, on_delete=models.CASCADE, related_name="projectiondefaults"
Projector, on_delete=models.PROTECT, related_name="projectiondefaults"
)
def get_root_rest_element(self):
@ -179,7 +183,7 @@ class ChatMessage(RESTModelMixin, models.Model):
timestamp = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=CASCADE_AND_AUTOUODATE)
class Meta:
default_permissions = ()
@ -269,7 +273,7 @@ class HistoryManager(models.Manager):
history_time = now()
for element in elements:
if (
element["disable_history"]
element.get("disable_history")
or element["collection_string"]
== self.model.get_collection_string()
):
@ -282,8 +286,8 @@ class HistoryManager(models.Manager):
element["collection_string"], element["id"]
),
now=history_time,
information=element["information"],
user_id=element["user_id"],
information=element.get("information", ""),
user_id=element.get("user_id"),
full_data=data,
)
instance.save(
@ -308,9 +312,6 @@ class HistoryManager(models.Manager):
id=full_data["id"],
collection_string=collection_string,
full_data=full_data,
information="",
user_id=None,
disable_history=False,
)
)
instances = self.add_elements(elements)
@ -336,7 +337,7 @@ class History(RESTModelMixin, models.Model):
information = models.CharField(max_length=255)
user = models.ForeignKey(
settings.AUTH_USER_MODEL, null=True, on_delete=models.SET_NULL
settings.AUTH_USER_MODEL, null=True, on_delete=SET_NULL_AND_AUTOUPDATE
)
full_data = models.OneToOneField(HistoryData, on_delete=models.CASCADE)

View File

@ -0,0 +1,23 @@
# Generated by Django 2.1.5 on 2019-01-19 13:25
from django.conf import settings
from django.db import migrations, models
import openslides.utils.models
class Migration(migrations.Migration):
dependencies = [("mediafiles", "0002_mediafile_private")]
operations = [
migrations.AlterField(
model_name="mediafile",
name="uploader",
field=models.ForeignKey(
null=True,
on_delete=openslides.utils.models.SET_NULL_AND_AUTOUPDATE,
to=settings.AUTH_USER_MODEL,
),
)
]

View File

@ -3,7 +3,7 @@ from django.db import models
from ..core.config import config
from ..utils.autoupdate import inform_changed_data
from ..utils.models import RESTModelMixin
from ..utils.models import SET_NULL_AND_AUTOUPDATE, RESTModelMixin
from .access_permissions import MediafileAccessPermissions
@ -25,7 +25,7 @@ class Mediafile(RESTModelMixin, models.Model):
"""A string representing the title of the file."""
uploader = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True
settings.AUTH_USER_MODEL, on_delete=SET_NULL_AND_AUTOUPDATE, null=True
)
"""A user the uploader of a file."""

View File

@ -6,20 +6,20 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('motions', '0018_auto_20190118_2101'),
]
dependencies = [("motions", "0018_auto_20190118_2101")]
operations = [
migrations.AddField(
model_name='motion',
name='created',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
model_name="motion",
name="created",
field=models.DateTimeField(
auto_now_add=True, default=django.utils.timezone.now
),
preserve_default=False,
),
migrations.AddField(
model_name='motion',
name='last_modified',
model_name="motion",
name="last_modified",
field=models.DateTimeField(auto_now=True),
),
]

View File

@ -0,0 +1,123 @@
# Generated by Django 2.1.5 on 2019-01-19 13:25
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
import openslides.utils.models
class Migration(migrations.Migration):
dependencies = [("motions", "0019_auto_20190119_1025")]
operations = [
migrations.AlterField(
model_name="motion",
name="category",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=openslides.utils.models.SET_NULL_AND_AUTOUPDATE,
to="motions.Category",
),
),
migrations.AlterField(
model_name="motion",
name="motion_block",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=openslides.utils.models.SET_NULL_AND_AUTOUPDATE,
to="motions.MotionBlock",
),
),
migrations.AlterField(
model_name="motion",
name="parent",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=openslides.utils.models.SET_NULL_AND_AUTOUPDATE,
related_name="amendments",
to="motions.Motion",
),
),
migrations.AlterField(
model_name="motion",
name="recommendation",
field=models.ForeignKey(
null=True,
on_delete=openslides.utils.models.SET_NULL_AND_AUTOUPDATE,
related_name="+",
to="motions.State",
),
),
migrations.AlterField(
model_name="motion",
name="sort_parent",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=openslides.utils.models.SET_NULL_AND_AUTOUPDATE,
related_name="children",
to="motions.Motion",
),
),
migrations.AlterField(
model_name="motion",
name="statute_paragraph",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=openslides.utils.models.SET_NULL_AND_AUTOUPDATE,
related_name="motions",
to="motions.StatuteParagraph",
),
),
migrations.AlterField(
model_name="motionchangerecommendation",
name="author",
field=models.ForeignKey(
null=True,
on_delete=openslides.utils.models.SET_NULL_AND_AUTOUPDATE,
to=settings.AUTH_USER_MODEL,
),
),
migrations.AlterField(
model_name="motionchangerecommendation",
name="motion",
field=models.ForeignKey(
on_delete=openslides.utils.models.CASCADE_AND_AUTOUODATE,
related_name="change_recommendations",
to="motions.Motion",
),
),
migrations.AlterField(
model_name="motionlog",
name="person",
field=models.ForeignKey(
null=True,
on_delete=openslides.utils.models.SET_NULL_AND_AUTOUPDATE,
to=settings.AUTH_USER_MODEL,
),
),
migrations.AlterField(
model_name="submitter",
name="user",
field=models.ForeignKey(
on_delete=openslides.utils.models.CASCADE_AND_AUTOUODATE,
to=settings.AUTH_USER_MODEL,
),
),
migrations.AlterField(
model_name="workflow",
name="first_state",
field=models.OneToOneField(
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="+",
to="motions.State",
),
),
]

View File

@ -23,6 +23,7 @@ from openslides.utils.autoupdate import inform_changed_data
from openslides.utils.exceptions import OpenSlidesError
from openslides.utils.models import RESTModelMixin
from ..utils.models import CASCADE_AND_AUTOUODATE, SET_NULL_AND_AUTOUPDATE
from .access_permissions import (
CategoryAccessPermissions,
MotionAccessPermissions,
@ -141,7 +142,7 @@ class Motion(RESTModelMixin, models.Model):
"""
recommendation = models.ForeignKey(
"State", related_name="+", on_delete=models.SET_NULL, null=True
"State", related_name="+", on_delete=SET_NULL_AND_AUTOUPDATE, null=True
)
"""
The recommendation of a person or committee for this motion.
@ -171,7 +172,7 @@ class Motion(RESTModelMixin, models.Model):
sort_parent = models.ForeignKey(
"self",
on_delete=models.SET_NULL,
on_delete=SET_NULL_AND_AUTOUPDATE,
null=True,
blank=True,
related_name="children",
@ -181,14 +182,14 @@ class Motion(RESTModelMixin, models.Model):
"""
category = models.ForeignKey(
"Category", on_delete=models.SET_NULL, null=True, blank=True
"Category", on_delete=SET_NULL_AND_AUTOUPDATE, null=True, blank=True
)
"""
ForeignKey to one category of motions.
"""
motion_block = models.ForeignKey(
"MotionBlock", on_delete=models.SET_NULL, null=True, blank=True
"MotionBlock", on_delete=SET_NULL_AND_AUTOUPDATE, null=True, blank=True
)
"""
ForeignKey to one block of motions.
@ -207,7 +208,7 @@ class Motion(RESTModelMixin, models.Model):
parent = models.ForeignKey(
"self",
on_delete=models.SET_NULL,
on_delete=SET_NULL_AND_AUTOUPDATE,
null=True,
blank=True,
related_name="amendments",
@ -220,7 +221,7 @@ class Motion(RESTModelMixin, models.Model):
statute_paragraph = models.ForeignKey(
StatuteParagraph,
on_delete=models.SET_NULL,
on_delete=SET_NULL_AND_AUTOUPDATE,
null=True,
blank=True,
related_name="motions",
@ -704,7 +705,7 @@ class Submitter(RESTModelMixin, models.Model):
Use custom Manager.
"""
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=CASCADE_AND_AUTOUODATE)
"""
ForeignKey to the user who is the submitter.
"""
@ -754,7 +755,7 @@ class MotionChangeRecommendation(RESTModelMixin, models.Model):
objects = MotionChangeRecommendationManager()
motion = models.ForeignKey(
Motion, on_delete=models.CASCADE, related_name="change_recommendations"
Motion, on_delete=CASCADE_AND_AUTOUODATE, related_name="change_recommendations"
)
"""The motion to which the change recommendation belongs."""
@ -780,7 +781,7 @@ class MotionChangeRecommendation(RESTModelMixin, models.Model):
"""The replacement for the section of the original text specified by motion, line_from and line_to"""
author = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True
settings.AUTH_USER_MODEL, on_delete=SET_NULL_AND_AUTOUPDATE, null=True
)
"""A user object, who created this change recommendation. Optional."""
@ -921,7 +922,7 @@ class MotionLog(RESTModelMixin, models.Model):
"""
person = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True
settings.AUTH_USER_MODEL, on_delete=SET_NULL_AND_AUTOUPDATE, null=True
)
"""A user object, who created the log message. Optional."""
@ -1192,7 +1193,7 @@ class Workflow(RESTModelMixin, models.Model):
"""A string representing the workflow."""
first_state = models.OneToOneField(
State, on_delete=models.SET_NULL, related_name="+", null=True, blank=True
State, on_delete=models.CASCADE, related_name="+", null=True
)
"""A one-to-one relation to a state, the starting point for the workflow."""

View File

@ -569,7 +569,11 @@ class MotionViewSet(ModelViewSet):
person=request.user,
skip_autoupdate=True,
)
inform_changed_data(motion, information=f"State set to {motion.state.name}.", user_id=request.user.pk)
inform_changed_data(
motion,
information=f"State set to {motion.state.name}.",
user_id=request.user.pk,
)
return Response({"detail": message})
@list_route(methods=["post"])
@ -1348,9 +1352,8 @@ class StateViewSet(
Customized view endpoint to delete a state.
"""
state = self.get_object()
if (
state.workflow.first_state.pk == state.pk
): # is this the first state of the workflow?
if state.workflow.first_state.pk == state.pk:
# is this the first state of the workflow?
raise ValidationError(
{"detail": "You cannot delete the first state of the workflow."}
)

View File

@ -5,19 +5,23 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('users', '0008_user_gender'),
]
dependencies = [("users", "0008_user_gender")]
operations = [
migrations.AlterModelOptions(
name='user',
name="user",
options={
'default_permissions': (), 'ordering': ('last_name', 'first_name', 'username'),
'permissions': (
('can_see_name', 'Can see names of users'),
('can_see_extra_data', 'Can see extra data of users (e.g. present and comment)'),
('can_change_password', 'Can change its own password'),
('can_manage', 'Can manage users'))},
),
"default_permissions": (),
"ordering": ("last_name", "first_name", "username"),
"permissions": (
("can_see_name", "Can see names of users"),
(
"can_see_extra_data",
"Can see extra data of users (e.g. present and comment)",
),
("can_change_password", "Can change its own password"),
("can_manage", "Can manage users"),
),
},
)
]

View File

@ -0,0 +1,22 @@
# Generated by Django 2.1.5 on 2019-01-19 13:47
from django.conf import settings
from django.db import migrations, models
import openslides.utils.models
class Migration(migrations.Migration):
dependencies = [("users", "0009_auto_20190119_0941")]
operations = [
migrations.AlterField(
model_name="personalnote",
name="user",
field=models.OneToOneField(
on_delete=openslides.utils.models.CASCADE_AND_AUTOUODATE,
to=settings.AUTH_USER_MODEL,
),
)
]

View File

@ -19,7 +19,7 @@ from jsonfield import JSONField
from ..core.config import config
from ..utils.auth import GROUP_ADMIN_PK
from ..utils.models import RESTModelMixin
from ..utils.models import CASCADE_AND_AUTOUODATE, RESTModelMixin
from .access_permissions import (
GroupAccessPermissions,
PersonalNoteAccessPermissions,
@ -330,7 +330,7 @@ class PersonalNote(RESTModelMixin, models.Model):
objects = PersonalNoteManager()
user = models.OneToOneField(User, on_delete=models.CASCADE)
user = models.OneToOneField(User, on_delete=CASCADE_AND_AUTOUODATE)
notes = JSONField()
class Meta:

View File

@ -369,8 +369,6 @@ class GroupViewSet(ModelViewSet):
id=full_data["id"],
collection_string=cachable.get_collection_string(),
full_data=full_data,
information="",
user_id=None,
disable_history=True,
)
)
@ -571,7 +569,10 @@ class SetPasswordView(APIView):
def post(self, request, *args, **kwargs):
user = request.user
if not (has_perm(user, "users.can_change_password") or has_perm(user, "users.can_manage")):
if not (
has_perm(user, "users.can_change_password")
or has_perm(user, "users.can_manage")
):
self.permission_denied(request)
if user.check_password(request.data["old_password"]):
try:
@ -602,7 +603,10 @@ class PasswordResetView(APIView):
"""
Loop over all users and send emails.
"""
if not (has_perm(request.user, "users.can_change_password") or has_perm(request.user, "users.can_manage")):
if not (
has_perm(request.user, "users.can_change_password")
or has_perm(request.user, "users.can_manage")
):
self.permission_denied(request)
to_email = request.data.get("email")
for user in self.get_users(to_email):
@ -671,7 +675,10 @@ class PasswordResetConfirmView(APIView):
http_method_names = ["post"]
def post(self, request, *args, **kwargs):
if not (has_perm(request.user, "users.can_change_password") or has_perm(request.user, "users.can_manage")):
if not (
has_perm(request.user, "users.can_change_password")
or has_perm(request.user, "users.can_manage")
):
self.permission_denied(request)
uidb64 = request.data.get("user_id")
token = request.data.get("token")

View File

@ -9,19 +9,33 @@ from mypy_extensions import TypedDict
from .cache import element_cache, get_element_id
from .projector import get_projectot_data
from .utils import get_model_from_collection_string
Element = TypedDict(
"Element",
{
"id": int,
"collection_string": str,
"full_data": Optional[Dict[str, Any]],
"information": str,
"user_id": Optional[int],
"disable_history": bool,
},
)
class ElementBase(TypedDict):
id: int
collection_string: str
full_data: Optional[Dict[str, Any]]
class Element(ElementBase, total=False):
"""
Data container to handle one root rest element for the autoupdate, history
and caching process.
The fields `id`, `collection_string` and `full_data` are required, the other
fields are optional.
if full_data is None, it means, that the element was deleted. If reload is
True, full_data is ignored and reloaded from the database later in the
process.
"""
information: str
user_id: Optional[int]
disable_history: bool
reload: bool
AutoupdateFormat = TypedDict(
"AutoupdateFormat",
@ -68,7 +82,6 @@ def inform_changed_data(
full_data=root_instance.get_full_data(),
information=information,
user_id=user_id,
disable_history=False,
)
bundle = autoupdate_bundle.get(threading.get_ident())
@ -100,7 +113,6 @@ def inform_deleted_data(
full_data=None,
information=information,
user_id=user_id,
disable_history=False,
)
bundle = autoupdate_bundle.get(threading.get_ident())
@ -201,6 +213,12 @@ def handle_changed_elements(elements: Iterable[Element]) -> None:
)
if elements:
for element in elements:
if element.get("reload"):
model = get_model_from_collection_string(element["collection_string"])
instance = model.objects.get(pk=element["id"])
element["full_data"] = instance.get_full_data()
# Save histroy here using sync code.
history_instances = save_history(elements)
@ -212,8 +230,6 @@ def handle_changed_elements(elements: Iterable[Element]) -> None:
id=history_instance.get_rest_pk(),
collection_string=history_instance.get_collection_string(),
full_data=history_instance.get_full_data(),
information="",
user_id=None,
disable_history=True, # This does not matter because history elements can never be part of the history itself.
)
)
@ -227,9 +243,8 @@ def handle_changed_elements(elements: Iterable[Element]) -> None:
)
def save_history(
elements: Iterable[Element]
) -> Iterable: # TODO: Try to write Iterable[History] here
def save_history(elements: Iterable[Element]) -> Iterable:
# TODO: Try to write Iterable[History] here
"""
Thin wrapper around the call of history saving manager method.

View File

@ -4,6 +4,7 @@ from django.core.exceptions import ImproperlyConfigured
from django.db import models
from .access_permissions import BaseAccessPermissions
from .autoupdate import Element, inform_changed_data, inform_changed_elements
from .rest_api import model_serializer_classes
from .utils import convert_camel_case_to_pseudo_snake_case
@ -153,3 +154,44 @@ class RESTModelMixin:
__import__(module_name)
serializer_class = model_serializer_classes[type(self)]
return serializer_class(self).data
def SET_NULL_AND_AUTOUPDATE(
collector: Any, field: Any, sub_objs: Any, using: Any
) -> None:
"""
Like models.SET_NULL but also informs the autoupdate system about the
instance that was reference.
"""
if len(sub_objs) != 1:
raise RuntimeError(
"SET_NULL_AND_AUTOUPDATE is used in an invalid usecase. Please report the bug!"
)
setattr(sub_objs[0], field.name, None)
inform_changed_data(sub_objs[0])
models.SET_NULL(collector, field, sub_objs, using)
def CASCADE_AND_AUTOUODATE(
collector: Any, field: Any, sub_objs: Any, using: Any
) -> None:
"""
Like models.SET_NULL but also informs the autoupdate system about the
root rest element of the also deleted instance.
"""
if len(sub_objs) != 1:
raise RuntimeError(
"CASCADE_AND_AUTOUPDATE is used in an invalid usecase. Please report the bug!"
)
root_rest_element = sub_objs[0].get_root_rest_element()
inform_changed_elements(
[
Element(
collection_string=root_rest_element.get_collection_string(),
id=root_rest_element.pk,
full_data=None,
reload=True,
)
]
)
models.CASCADE(collector, field, sub_objs, using)

View File

@ -80,8 +80,6 @@ async def set_config():
id=config_id,
collection_string=collection_string,
full_data=full_data,
information="",
user_id=None,
disable_history=True,
)
]