Opened 3 years ago

Closed 3 years ago

Last modified 3 years ago

#33307 closed Uncategorized (invalid)

Model got both positional and keyword arguments for field 'id'.

Reported by: אורי Owned by: nobody
Component: Database layer (models, ORM) Version: 4.0
Severity: Normal Keywords:
Cc: אורי Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by אורי)

Hi, I got failed tests with Django==4.0rc1 and I suspect this is related to Django. The tests pass with Django==3.2.9.

Here is one of the failed tests (on Windows, with Python 3.8):

======================================================================
ERROR: test_user_can_read_chat_with_a_blocker (speedy.core.messages.tests.test_views.ChatDetailViewTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "speedy\core\messages\tests\test_views.py", line 78, in test_user_can_read_chat_with_a_blocker
    self.client.login(username=self.user_1.slug, password=tests_settings.USER_PASSWORD)
  File ".venv_3.8\lib\site-packages\django\test\client.py", line 604, in login
    user = authenticate(**credentials)
  File ".venv_3.8\lib\site-packages\django\views\decorators\debug.py", line 42, in sensitive_variables_wrapper
    return func(*func_args, **func_kwargs)
  File ".venv_3.8\lib\site-packages\django\contrib\auth\__init__.py", line 76, in authenticate
    user = backend.authenticate(request, **credentials)
  File ".venv_3.8\lib\site-packages\django\contrib\auth\backends.py", line 42, in authenticate
    user = UserModel._default_manager.get_by_natural_key(username)
  File "speedy\core\accounts\managers.py", line 37, in get_by_natural_key
    return self.distinct().get(Q(username=normalize_username(username=username)) | Q(email_addresses__email=username))
  File ".venv_3.8\lib\site-packages\django\db\models\query.py", line 435, in get
    num = len(clone)
  File ".venv_3.8\lib\site-packages\django\db\models\query.py", line 262, in __len__
    self._fetch_all()
  File ".venv_3.8\lib\site-packages\django\db\models\query.py", line 1356, in _fetch_all
    self._prefetch_related_objects()
  File ".venv_3.8\lib\site-packages\django\db\models\query.py", line 841, in _prefetch_related_objects
    prefetch_related_objects(self._result_cache, *self._prefetch_related_lookups)
  File ".venv_3.8\lib\site-packages\django\db\models\query.py", line 1760, in prefetch_related_objects
    obj_list, additional_lookups = prefetch_one_level(
  File ".venv_3.8\lib\site-packages\django\db\models\query.py", line 1902, in prefetch_one_level
    all_related_objects = list(rel_qs)
  File ".venv_3.8\lib\site-packages\django\db\models\query.py", line 280, in __iter__
    self._fetch_all()
  File ".venv_3.8\lib\site-packages\django\db\models\query.py", line 1354, in _fetch_all
    self._result_cache = list(self._iterable_class(self))
  File ".venv_3.8\lib\site-packages\django\db\models\query.py", line 69, in __iter__
    obj = model_cls.from_db(db, init_list, row[model_fields_start:model_fields_end])
  File ".venv_3.8\lib\site-packages\django\db\models\base.py", line 519, in from_db
    new = cls(*values)
  File "speedy\core\uploads\models.py", line 31, in __init__
    super().__init__(*args, **kwargs)
  File ".venv_3.8\lib\site-packages\django\db\models\base.py", line 446, in __init__
    raise TypeError(
TypeError: Image() got both positional and keyword arguments for field 'id'.

======================================================================

Our repository is open source at https://github.com/speedy-net/speedy-net

This might be related to our models:

import os

from django.db import models
from django.utils.translation import gettext_lazy as _

from speedy.core.base.models import TimeStampedModel
from speedy.core.base.fields import RegularUDIDField
from speedy.core.base.utils import generate_regular_udid
from .utils import uuid_dir


class File(TimeStampedModel):
    id = RegularUDIDField()
    owner = models.ForeignKey(to='accounts.Entity', verbose_name=_('owner'), on_delete=models.SET_NULL, blank=True, null=True)
    file = models.FileField(verbose_name=_('file'), upload_to=uuid_dir)
    is_stored = models.BooleanField(verbose_name=_('is stored'), default=False)
    size = models.PositiveIntegerField(verbose_name=_('file size'), default=0)

    @property
    def basename(self):
        return os.path.basename(self.file.name)

    class Meta:
        verbose_name = _('file')
        verbose_name_plural = _('uploaded files')
        ordering = ('-date_created',)

    def __init__(self, *args, **kwargs):
        if (not (kwargs.get('id'))):
            kwargs['id'] = generate_regular_udid()
        super().__init__(*args, **kwargs)

    def __str__(self):
        return '{} (owner={})'.format(self.basename, self.owner)

    def save(self, *args, **kwargs):
        self.size = self.file.size
        return super().save(*args, **kwargs)

    def store(self):
        self.is_stored = True
        self.save(update_fields={'is_stored', 'size'})


class Image(File):
    visible_on_website = models.BooleanField(verbose_name=_('visible on website'), default=False)
    aws_image_moderation_time = models.DateTimeField(verbose_name=_('AWS image moderation time'), blank=True, null=True)
    aws_facial_analysis_time = models.DateTimeField(verbose_name=_('AWS facial analysis time'), blank=True, null=True)
    aws_raw_image_moderation_results = models.JSONField(verbose_name=_('AWS raw image moderation results'), blank=True, null=True)
    aws_raw_facial_analysis_results = models.JSONField(verbose_name=_('AWS raw facial analysis results'), blank=True, null=True)
    number_of_faces = models.PositiveSmallIntegerField(verbose_name=_('number of faces'), blank=True, null=True)

    class Meta:
        verbose_name = _('images')
        verbose_name_plural = _('uploaded images')
        ordering = ('-date_created',)
from django.conf import settings as django_settings
from django.db import models
from django.utils.translation import gettext_lazy as _

from .utils import generate_regular_udid, generate_small_udid
from . import validators as speedy_core_base_validators


# Never use this class directly. Only use inherited classes below.
class UDIDField(models.CharField):
    class Meta:
        abstract = True

    def __init__(self, *args, **kwargs):
        given_kwargs = kwargs
        defaults = {
            'verbose_name': _('ID'),
            'primary_key': True,
            'db_index': True,
            'unique': True,
        }
        kwargs = defaults
        kwargs.update(given_kwargs)
        super().__init__(*args, **kwargs)


class SmallUDIDField(UDIDField):
    id_generator = staticmethod(generate_small_udid)

    def __init__(self, *args, **kwargs):
        given_kwargs = kwargs
        defaults = {
            'max_length': django_settings.SMALL_UDID_LENGTH,
            'validators': [speedy_core_base_validators.small_udid_validator],
        }
        kwargs = defaults
        kwargs.update(given_kwargs)
        super().__init__(*args, **kwargs)


class RegularUDIDField(UDIDField):
    id_generator = staticmethod(generate_regular_udid)

    def __init__(self, *args, **kwargs):
        given_kwargs = kwargs
        defaults = {
            'max_length': django_settings.REGULAR_UDID_LENGTH,
            'validators': [speedy_core_base_validators.regular_udid_validator],
        }
        kwargs = defaults
        kwargs.update(given_kwargs)
        super().__init__(*args, **kwargs)

Change History (7)

comment:1 by אורי, 3 years ago

Description: modified (diff)

comment:2 by אורי, 3 years ago

Cc: אורי added

comment:3 by אורי, 3 years ago

I see the line (31) is related to the following method:

    def __init__(self, *args, **kwargs):
        if (not (kwargs.get('id'))):
            kwargs['id'] = generate_regular_udid()
        super().__init__(*args, **kwargs)

However, this works with Django<=3.2.9.

comment:4 by Mariusz Felisiak, 3 years ago

Component: UncategorizedDatabase layer (models, ORM)
Resolution: invalid
Status: newclosed

Thanks for the report. It's an intended change, see 73b1b225ce1a3318d4478f90cc0db0a260aba3aa and #22640. This error was added to prevent misconfigurations when values for the same fields are passed in args and kwargs as in your case. I'd recommend moving this logic to the save().

Last edited 3 years ago by Mariusz Felisiak (previous) (diff)

comment:5 by אורי, 3 years ago

What is the case of starting an instance with an id in args? Is it a real id or just None? I think we need the id to be set before calling save(), we use the id for generating the file name and if it's just None in args then we need to generate it.

in reply to:  5 comment:6 by Mariusz Felisiak, 3 years ago

Replying to אורי:

What is the case of starting an instance with an id in args? Is it a real id or just None? I think we need the id to be set before calling save(), we use the id for generating the file name and if it's just None in args then we need to generate it.

When calling get() an instance is initialize with id from the database, so previously id in kwargs was ignored, e.g.

>>> x = File.objects.create()
>>> x.id
98176428171628647286
>>> x = File.objects.get() # <- Here your code generates another "id" that was ignored
>>> # super().__init__() was called with File('98176428171628647286', id='some_other_id')
>>> x.id
98176428171628647286

comment:7 by אורי, 3 years ago

I understand, thank you. I was not aware that our id was ignored.

Note: See TracTickets for help on using tickets.
Back to Top