Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-model-bakery for
openSUSE:Factory checked in at 2025-04-11 19:27:23
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-model-bakery (Old)
and /work/SRC/openSUSE:Factory/.python-model-bakery.new.1907 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-model-bakery"
Fri Apr 11 19:27:23 2025 rev:7 rq:1268609 version:1.20.4
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-model-bakery/python-model-bakery.changes
2024-11-20 17:00:54.215124138 +0100
+++
/work/SRC/openSUSE:Factory/.python-model-bakery.new.1907/python-model-bakery.changes
2025-04-11 19:27:24.963120168 +0200
@@ -1,0 +2,17 @@
+Fri Apr 11 12:46:10 UTC 2025 - Dirk Müller <[email protected]>
+
+- update to 1.20.4:
+ * Fix regression introduced in 1.20.3 that prevented using
+ `auto_now` and `auto_now_add` fields with seq or callable.
+ * Fix support of `auto_now` and `auto_now_add` fields in
+ combination with `_fill_optional`
+ * Isolate Recipe defaults to prevent modification via instances
+ * Fix setting GFK parameter by a callable
+ * Fix regression forbidding using Proxy models as GFK
+ * docs: Add missing doc on `_refresh_after_create` option
+ * Fix `Recipe.prepare` without `_quantity` (on one-to-one
+ relation)
+ * Remove deprecation warning of
+ `datetime.datetime.utcfromtimestamp`.
+
+-------------------------------------------------------------------
Old:
----
model-bakery-1.20.0-gh.tar.gz
New:
----
model-bakery-1.20.4-gh.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-model-bakery.spec ++++++
--- /var/tmp/diff_new_pack.o2nVmr/_old 2025-04-11 19:27:25.599146801 +0200
+++ /var/tmp/diff_new_pack.o2nVmr/_new 2025-04-11 19:27:25.603146969 +0200
@@ -1,7 +1,7 @@
#
# spec file for package python-model-bakery
#
-# Copyright (c) 2024 SUSE LLC
+# Copyright (c) 2025 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -18,7 +18,7 @@
%{?sle15_python_module_pythons}
Name: python-model-bakery
-Version: 1.20.0
+Version: 1.20.4
Release: 0
Summary: Smart object creation facility for Django
License: Apache-2.0
++++++ model-bakery-1.20.0-gh.tar.gz -> model-bakery-1.20.4-gh.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/model_bakery-1.20.0/.readthedocs.yaml
new/model_bakery-1.20.4/.readthedocs.yaml
--- old/model_bakery-1.20.0/.readthedocs.yaml 2024-10-10 10:08:17.000000000
+0200
+++ new/model_bakery-1.20.4/.readthedocs.yaml 2025-02-26 21:08:35.000000000
+0100
@@ -11,3 +11,6 @@
path: .
extra_requirements:
- docs
+
+sphinx:
+ configuration: docs/conf.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/model_bakery-1.20.0/CHANGELOG.md
new/model_bakery-1.20.4/CHANGELOG.md
--- old/model_bakery-1.20.0/CHANGELOG.md 2024-10-10 10:08:17.000000000
+0200
+++ new/model_bakery-1.20.4/CHANGELOG.md 2025-02-26 21:08:35.000000000
+0100
@@ -13,6 +13,34 @@
### Removed
+## [1.20.4](https://pypi.org/project/model-bakery/1.20.4/)
+
+### Changed
+- Fix regression introduced in 1.20.3 that prevented using `auto_now` and
`auto_now_add` fields with seq or callable.
+
+## [1.20.3](https://pypi.org/project/model-bakery/1.20.3/)
+
+### Changed
+- Fix support of `auto_now` and `auto_now_add` fields in combination with
`_fill_optional`
+- Isolate Recipe defaults to prevent modification via instances
+
+## [1.20.2](https://pypi.org/project/model-bakery/1.20.2/)
+
+### Changed
+- Fix setting GFK parameter by a callable
+- Fix regression forbidding using Proxy models as GFK
+
+## [1.20.1](https://pypi.org/project/model-bakery/1.20.1/)
+
+### Added
+- docs: Add missing doc on `_refresh_after_create` option
+
+### Changed
+- Fix `Recipe.prepare` without `_quantity` (on one-to-one relation)
+
+### Removed
+- Remove deprecation warning of `datetime.datetime.utcfromtimestamp`.
+
## [1.20.0](https://pypi.org/project/model-bakery/1.20.0/)
### Added
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/model_bakery-1.20.0/docs/basic_usage.md
new/model_bakery-1.20.4/docs/basic_usage.md
--- old/model_bakery-1.20.0/docs/basic_usage.md 2024-10-10 10:08:17.000000000
+0200
+++ new/model_bakery-1.20.4/docs/basic_usage.md 2025-02-26 21:08:35.000000000
+0100
@@ -242,6 +242,24 @@
**Important**: the lib does not do any kind of file clean up, so it's up to
you to delete the files created by it.
+## Refreshing Instances After Creation
+
+By default, Model Bakery does not refresh the instance after it is created and
saved.
+If you want to refresh the instance after it is created,
+you can pass the flag `_refresh_after_create=True` to either `baker.make` or
`baker.make_recipe`.
+This ensures that any changes made by the database or signal handlers are
reflected in the instance.
+
+```python
+from model_bakery import baker
+
+# default behavior
+customer = baker.make('shop.Customer', birthday='1990-01-01',
_refresh_after_create=False)
+assert customer.birthday == '1990-01-01'
+
+customer = baker.make('shop.Customer', birthday='1990-01-01',
_refresh_after_create=True)
+assert customer.birthday == datetime.date(1990, 1, 1)
+```
+
## Non persistent objects
If you don't need a persisted object, Model Bakery can handle this for you as
well with the **prepare** method:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/model_bakery-1.20.0/model_bakery/__about__.py
new/model_bakery-1.20.4/model_bakery/__about__.py
--- old/model_bakery-1.20.0/model_bakery/__about__.py 2024-10-10
10:08:17.000000000 +0200
+++ new/model_bakery-1.20.4/model_bakery/__about__.py 2025-02-26
21:08:35.000000000 +0100
@@ -1 +1 @@
-__version__ = "1.20.0"
+__version__ = "1.20.4"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/model_bakery-1.20.0/model_bakery/baker.py
new/model_bakery-1.20.4/model_bakery/baker.py
--- old/model_bakery-1.20.0/model_bakery/baker.py 2024-10-10
10:08:17.000000000 +0200
+++ new/model_bakery-1.20.4/model_bakery/baker.py 2025-02-26
21:08:35.000000000 +0100
@@ -70,6 +70,10 @@
return quantity is not None and (not isinstance(quantity, int) or quantity
< 1)
+def _is_auto_datetime_field(field: Field) -> bool:
+ return getattr(field, "auto_now_add", False) or getattr(field, "auto_now",
False)
+
+
def seed(seed: Union[int, float, str, bytes, bytearray, None]) -> None:
Baker.seed(seed)
@@ -513,10 +517,7 @@
if isinstance(field, ForeignRelatedObjectsDescriptor):
one_to_many_keys[k] = attrs.pop(k)
- if hasattr(field, "field") and (
- getattr(field.field, "auto_now_add", False)
- or getattr(field.field, "auto_now", False)
- ):
+ if hasattr(field, "field") and
_is_auto_datetime_field(field.field):
auto_now_keys[k] = attrs[k]
if BAKER_CONTENTTYPES and isinstance(field, GenericForeignKey):
@@ -643,8 +644,13 @@
if not attrs:
return
+ # use .update() to force update auto_now fields
instance.__class__.objects.filter(pk=instance.pk).update(**attrs)
+ # to make the resulting instance has the specified values
+ for k, v in attrs.items():
+ setattr(instance, k, v)
+
def _handle_one_to_many(self, instance: Model, attrs: Dict[str, Any]):
for key, values in attrs.items():
manager = getattr(instance, key)
@@ -699,6 +705,8 @@
ct_field_name = data["content_type_field"]
oid_field_name = data["object_id_field"]
value = data["value"]
+ if callable(value):
+ value = value()
if is_iterator(value):
value = next(value)
if value is None:
@@ -714,7 +722,9 @@
setattr(
instance,
ct_field_name,
-
contenttypes_models.ContentType.objects.get_for_model(value),
+ contenttypes_models.ContentType.objects.get_for_model(
+ value, for_concrete_model=False
+ ),
)
setattr(instance, oid_field_name, value.pk)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/model_bakery-1.20.0/model_bakery/recipe.py
new/model_bakery-1.20.4/model_bakery/recipe.py
--- old/model_bakery-1.20.0/model_bakery/recipe.py 2024-10-10
10:08:17.000000000 +0200
+++ new/model_bakery-1.20.4/model_bakery/recipe.py 2025-02-26
21:08:35.000000000 +0100
@@ -1,4 +1,5 @@
import collections
+import copy
import itertools
from typing import (
Any,
@@ -79,6 +80,8 @@
mapping[k] = v.recipe.prepare(_using=_using,
**recipe_attrs)
elif isinstance(v, related):
mapping[k] = v.make
+ elif isinstance(v, collections.abc.Container):
+ mapping[k] = copy.deepcopy(v)
mapping.update(new_attrs)
mapping.update(rel_fields_attrs)
@@ -164,9 +167,11 @@
**attrs: Any,
) -> Union[M, List[M]]:
defaults = {
- "_quantity": _quantity,
"_save_related": _save_related,
}
+ if _quantity is not None:
+ defaults["_quantity"] = _quantity # type: ignore[assignment]
+
defaults.update(attrs)
return baker.prepare(
self._model, _using=_using, **self._mapping(_using, defaults)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/model_bakery-1.20.0/model_bakery/timezone.py
new/model_bakery-1.20.4/model_bakery/timezone.py
--- old/model_bakery-1.20.0/model_bakery/timezone.py 2024-10-10
10:08:17.000000000 +0200
+++ new/model_bakery-1.20.4/model_bakery/timezone.py 2025-02-26
21:08:35.000000000 +0100
@@ -8,6 +8,6 @@
def tz_aware(value: datetime) -> datetime:
"""Return an UTC-aware datetime in case of USE_TZ=True."""
if settings.USE_TZ:
- value = value.replace(tzinfo=timezone.utc)
+ return value.replace(tzinfo=timezone.utc)
- return value
+ return value.replace(tzinfo=None)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/model_bakery-1.20.0/model_bakery/utils.py
new/model_bakery-1.20.4/model_bakery/utils.py
--- old/model_bakery-1.20.0/model_bakery/utils.py 2024-10-10
10:08:17.000000000 +0200
+++ new/model_bakery-1.20.4/model_bakery/utils.py 2025-02-26
21:08:35.000000000 +0100
@@ -86,7 +86,10 @@
start = (date - epoch_datetime).total_seconds()
increment_by = increment_by.total_seconds()
for n in itertools.count(increment_by, increment_by):
- series_date = tz_aware(datetime.datetime.utcfromtimestamp(start +
n))
+ series_date = tz_aware(
+ datetime.datetime.fromtimestamp(start + n,
tz=datetime.timezone.utc)
+ )
+
if type(value) is datetime.time:
yield series_date.time()
elif type(value) is datetime.date:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/model_bakery-1.20.0/tests/test_baker.py
new/model_bakery-1.20.4/tests/test_baker.py
--- old/model_bakery-1.20.0/tests/test_baker.py 2024-10-10 10:08:17.000000000
+0200
+++ new/model_bakery-1.20.4/tests/test_baker.py 2025-02-26 21:08:35.000000000
+0100
@@ -19,7 +19,7 @@
ModelNotFound,
)
from model_bakery.timezone import tz_aware
-from tests.generic import models
+from tests.generic import baker_recipes, models
from tests.generic.forms import DummyGenericIPAddressFieldForm
@@ -286,6 +286,12 @@
assert lonely_person.pk is None
assert lonely_person.only_friend.pk
+ def test_recipe_prepare_model_with_one_to_one_and_save_related(self):
+ lonely_person = baker_recipes.lonely_person.prepare(_save_related=True)
+
+ assert lonely_person.pk is None
+ assert lonely_person.only_friend.pk
+
@pytest.mark.django_db
class TestBakerCreatesAssociatedModels(TestCase):
@@ -629,6 +635,38 @@
assert isinstance(dummy, models.DummyGenericForeignKeyModel)
assert isinstance(dummy.content_type, ContentType)
+ def test_create_model_with_contenttype_with_content_object(self):
+ """Test creating model with contenttype field and populating that
field by function."""
+ from django.contrib.contenttypes.models import ContentType
+
+ def get_dummy_key():
+ return baker.make("Person")
+
+ dummy = baker.make(
+ models.DummyGenericForeignKeyModel, content_object=get_dummy_key
+ )
+ assert isinstance(dummy, models.DummyGenericForeignKeyModel)
+ assert isinstance(dummy.content_type, ContentType)
+ assert isinstance(dummy.content_object, models.Person)
+
+ def test_create_model_with_contenttype_field_and_proxy_model(self):
+ from django.contrib.contenttypes.models import ContentType
+
+ class ProxyPerson(models.Person):
+ class Meta:
+ proxy = True
+ app_label = "generic"
+
+ dummy = baker.make(
+ models.DummyGenericForeignKeyModel,
+ content_object=baker.make(ProxyPerson, name="John Doe"),
+ )
+ dummy.refresh_from_db()
+ assert isinstance(dummy, models.DummyGenericForeignKeyModel)
+ assert isinstance(dummy.content_type, ContentType)
+ assert isinstance(dummy.content_object, ProxyPerson)
+ assert dummy.content_object.name == "John Doe"
+
@pytest.mark.skipif(
not BAKER_CONTENTTYPES, reason="Django contenttypes framework is not
installed"
@@ -1119,8 +1157,30 @@
sent_date=now,
)
- instance.refresh_from_db()
+ assert instance.created == now
+ assert instance.updated == now
+ assert instance.sent_date == now
+ # Should not update after refreshing from the db
+ instance.refresh_from_db()
assert instance.created == now
assert instance.updated == now
assert instance.sent_date == now
+
+ @pytest.mark.django_db
+ def test_make_with_auto_now_and_fill_optional(self):
+ instance = baker.make(
+ models.ModelWithAutoNowFields,
+ _fill_optional=True,
+ )
+ created, updated, sent_date = (
+ instance.created,
+ instance.updated,
+ instance.sent_date,
+ )
+
+ # Should not update after refreshing from the db
+ instance.refresh_from_db()
+ assert instance.created == created
+ assert instance.updated == updated
+ assert instance.sent_date == sent_date
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/model_bakery-1.20.0/tests/test_recipes.py
new/model_bakery-1.20.4/tests/test_recipes.py
--- old/model_bakery-1.20.0/tests/test_recipes.py 2024-10-10
10:08:17.000000000 +0200
+++ new/model_bakery-1.20.4/tests/test_recipes.py 2025-02-26
21:08:35.000000000 +0100
@@ -4,6 +4,7 @@
from random import choice # noqa
from unittest.mock import patch
+from django.db import connection
from django.utils.timezone import now
import pytest
@@ -19,6 +20,7 @@
DummyBlankFieldsModel,
DummyNumbersModel,
LonelyPerson,
+ ModelWithAutoNowFields,
Person,
Profile,
User,
@@ -34,6 +36,7 @@
"blog": "https://joe.example.com",
"days_since_last_login": 4,
"birth_time": now(),
+ "data": {"one": 1},
}
person_recipe = Recipe(Person, **recipe_attrs)
user_recipe = Recipe(User)
@@ -68,6 +71,8 @@
assert person.appointment == recipe_attrs["appointment"]
assert person.blog == recipe_attrs["blog"]
assert person.days_since_last_login ==
recipe_attrs["days_since_last_login"]
+ assert person.data is not recipe_attrs["data"]
+ assert person.data == recipe_attrs["data"]
assert person.id is not None
def test_flat_model_prepare_recipe_with_the_correct_attributes(self):
@@ -80,6 +85,8 @@
assert person.appointment == recipe_attrs["appointment"]
assert person.blog == recipe_attrs["blog"]
assert person.days_since_last_login ==
recipe_attrs["days_since_last_login"]
+ assert person.data is not recipe_attrs["data"]
+ assert person.data == recipe_attrs["data"]
assert person.id is None
def test_accepts_callable(self):
@@ -171,6 +178,34 @@
except AttributeError as e:
pytest.fail(f"{e}")
+ def test_recipe_dict_attribute_isolation(self):
+ person1 = person_recipe.make()
+ person2 = person_recipe.make()
+ person2.data["two"] = 2
+ person3 = person_recipe.make()
+
+ # Mutation on instances must have no side effect on their recipe
definition,
+ # or on other instances of the same recipe.
+ assert person1.data == {"one": 1}
+ assert person2.data == {"one": 1, "two": 2}
+ assert person3.data == {"one": 1}
+
+ @pytest.mark.skipif(
+ connection.vendor != "postgresql", reason="PostgreSQL specific tests"
+ )
+ def test_recipe_list_attribute_isolation(self):
+ pg_person_recipe = person_recipe.extend(acquaintances=[1, 2, 3])
+ person1 = pg_person_recipe.make()
+ person2 = pg_person_recipe.make()
+ person2.acquaintances.append(4)
+ person3 = pg_person_recipe.make()
+
+ # Mutation on instances must have no side effect on their recipe
definition,
+ # or on other instances of the same recipe.
+ assert person1.acquaintances == [1, 2, 3]
+ assert person2.acquaintances == [1, 2, 3, 4]
+ assert person3.acquaintances == [1, 2, 3]
+
@pytest.mark.django_db
class TestExecutingRecipes:
@@ -635,3 +670,37 @@
DummyBlankFieldsModel, blank_text_field="not an iterator, so don't
iterate!"
)
assert r.make().blank_text_field == "not an iterator, so don't
iterate!"
+
+
+class TestAutoNowFields:
+ @pytest.mark.django_db
+ def test_make_with_auto_now_using_datetime_generator(self):
+ delta = timedelta(minutes=1)
+
+ def gen():
+ idx = 0
+ while True:
+ idx += 1
+ yield tz_aware(TEST_TIME) + idx * delta
+
+ r = Recipe(
+ ModelWithAutoNowFields,
+ created=gen(),
+ )
+
+ assert r.make().created == tz_aware(TEST_TIME + 1 * delta)
+ assert r.make().created == tz_aware(TEST_TIME + 2 * delta)
+
+ @pytest.mark.django_db
+ def test_make_with_auto_now_using_datetime_seq(self):
+ delta = timedelta(minutes=1)
+ r = Recipe(
+ ModelWithAutoNowFields,
+ created=seq(
+ tz_aware(TEST_TIME),
+ increment_by=delta,
+ ),
+ )
+
+ assert r.make().created == tz_aware(TEST_TIME + 1 * delta)
+ assert r.make().created == tz_aware(TEST_TIME + 2 * delta)