Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion django/contrib/admin/static/admin/css/widgets.css
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,8 @@ ul.timelist, .timelist li {
.inline-deletelink {
float: right;
text-indent: -9999px;
background: url(../img/inline-delete.svg) 0 0 no-repeat;
background: url(../img/inline-delete.svg) center center no-repeat;
background-size: contain;
width: 1.5rem;
height: 1.5rem;
border: 0px none;
Expand Down
5 changes: 1 addition & 4 deletions django/db/models/sql/subqueries.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,12 @@ def add_update_values(self, values):
values_seq = []
for name, val in values.items():
field = self.get_meta().get_field(name)
direct = (
not (field.auto_created and not field.concrete) or not field.concrete
)
model = field.model._meta.concrete_model
if field.name == "pk" and model._meta.is_composite_pk:
raise FieldError(
"Composite primary key fields must be updated individually."
)
if not direct or (field.is_relation and field.many_to_many):
if not field.concrete or (field.is_relation and field.many_to_many):
raise FieldError(
"Cannot update model field %r (only non-relations and "
"foreign keys permitted)." % field
Expand Down
4 changes: 3 additions & 1 deletion django/template/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,12 @@

import inspect
import logging
import os
import re
import warnings
from enum import Enum

import django
from django.template.context import BaseContext
from django.utils.formats import localize
from django.utils.html import conditional_escape
Expand Down Expand Up @@ -327,7 +329,7 @@ def source(self):
"PartialTemplate.source is only available when template "
"debugging is enabled.",
RuntimeWarning,
stacklevel=2,
skip_file_prefixes=(os.path.dirname(django.__file__),),
)
return self.find_partial_source(template.source)

Expand Down
14 changes: 13 additions & 1 deletion docs/ref/django-admin.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1319,6 +1319,15 @@ zip files, you can use a URL like:

django-admin startapp --template=https://github.com/githubuser/django-app-template/archive/main.zip myapp

.. warning::

Templates provided via ``--template`` are used as is. Malicious or poorly
constructed templates may introduce security weaknesses or unintended
behavior. Compressed archives may also consume excessive resources during
extraction, potentially causing crashes or hangs.

Contents of templates should be carefully inspected before use.

.. django-admin-option:: --extension EXTENSIONS, -e EXTENSIONS

Specifies which file extensions in the app template should be rendered with the
Expand Down Expand Up @@ -1412,7 +1421,10 @@ For example:
.. django-admin-option:: --template TEMPLATE

Specifies a directory, file path, or URL of a custom project template. See the
:option:`startapp --template` documentation for examples and usage.
:option:`startapp --template` documentation for examples and usage. The same
**security considerations** described for ``startapp`` templates apply here:
malicious or poorly constructed templates may introduce weaknesses or consume
excessive resources, and templates should be carefully inspected before use.

.. django-admin-option:: --extension EXTENSIONS, -e EXTENSIONS

Expand Down
11 changes: 7 additions & 4 deletions tests/composite_pk/test_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,13 +169,16 @@ def test_update_token_by_tenant_name(self):
token_3 = Token.objects.get(pk=self.token_3.pk)
self.assertEqual(token_3.secret, "bar")

def test_cant_update_to_unsaved_object(self):
def test_cant_update_relation(self):
msg = (
"Unsaved model instance <User: User object ((None, None))> cannot be used "
"in an ORM query."
"Cannot update model field <django.db.models.fields.related.ForeignObject: "
"user> (only non-relations and foreign keys permitted)"
)

with self.assertRaisesMessage(ValueError, msg):
with self.assertRaisesMessage(FieldError, msg):
Comment.objects.update(user=self.user_1)

with self.assertRaisesMessage(FieldError, msg):
Comment.objects.update(user=User())

def test_cant_update_pk_field(self):
Expand Down
3 changes: 2 additions & 1 deletion tests/template_tests/test_partials.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,9 @@ def test_template_source_warning(self):
RuntimeWarning,
"PartialTemplate.source is only available when template "
"debugging is enabled.",
):
) as ctx:
self.assertEqual(partial.template.source, "")
self.assertEqual(ctx.filename, __file__)


class RobustPartialHandlingTests(TestCase):
Expand Down
3 changes: 3 additions & 0 deletions tests/update/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ class Foo(models.Model):

class Bar(models.Model):
foo = models.ForeignKey(Foo, models.CASCADE, to_field="target")
o2o_foo = models.OneToOneField(
Foo, models.CASCADE, related_name="o2o_bar", null=True
)
m2m_foo = models.ManyToManyField(Foo, related_name="m2m_foo")
x = models.IntegerField(default=0)

Expand Down
32 changes: 32 additions & 0 deletions tests/update/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,38 @@ def test_update_m2m_field(self):
with self.assertRaisesMessage(FieldError, msg):
Bar.objects.update(m2m_foo="whatever")

def test_update_reverse_m2m_descriptor(self):
msg = (
"Cannot update model field <ManyToManyRel: update.bar> "
"(only non-relations and foreign keys permitted)."
)
with self.assertRaisesMessage(FieldError, msg):
Foo.objects.update(m2m_foo="whatever")

def test_update_reverse_fk_descriptor(self):
msg = (
"Cannot update model field <ManyToOneRel: update.bar> "
"(only non-relations and foreign keys permitted)."
)
with self.assertRaisesMessage(FieldError, msg):
Foo.objects.update(bar="whatever")

def test_update_reverse_o2o_descriptor(self):
msg = (
"Cannot update model field <OneToOneRel: update.bar> "
"(only non-relations and foreign keys permitted)."
)
with self.assertRaisesMessage(FieldError, msg):
Foo.objects.update(o2o_bar="whatever")

def test_update_reverse_mti_parent_link_descriptor(self):
msg = (
"Cannot update model field <OneToOneRel: update.uniquenumberchild> "
"(only non-relations and foreign keys permitted)."
)
with self.assertRaisesMessage(FieldError, msg):
UniqueNumber.objects.update(uniquenumberchild="whatever")

def test_update_transformed_field(self):
A.objects.create(x=5)
A.objects.create(x=-6)
Expand Down
Loading