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 <dmuel...@suse.com>
+
+- 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)

Reply via email to