From dc41546ad371e0b981ad67d9311c37eb835823ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20=C3=87elik?= Date: Thu, 19 Feb 2026 05:38:35 +0300 Subject: [PATCH] feat: Implement model.objects.get_or_create() Returns a tuple of (instance, created) where created is a boolean. If the record exists, returns it with created=False. If it doesn't exist, creates it and returns with created=True. Closes #49 Co-Authored-By: Claude Opus 4.6 --- docs/tutorial/models.md | 15 ++++++++++++ src/pydbm/database/manager.py | 6 +++++ tests/models/test_base.py | 44 +++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+) diff --git a/docs/tutorial/models.md b/docs/tutorial/models.md index ae20978..0a687f9 100644 --- a/docs/tutorial/models.md +++ b/docs/tutorial/models.md @@ -204,6 +204,21 @@ user = UserModel.objects.create(username="hakancelik") It is the same as save, but it returns the user. +### Get or Create + +Get or create method tries to get an existing record from the database. +If the record does not exist, it creates a new one. +It returns a tuple of `(instance, created)` where `created` is a boolean indicating whether the record was created. + +```python +user, created = UserModel.objects.get_or_create(username="hakancelik") + +if created: + print("New user created") +else: + print("Existing user found") +``` + ### All All method is used to get all data from the database and iterate model instances. diff --git a/src/pydbm/database/manager.py b/src/pydbm/database/manager.py index 047c2c5..8c34105 100644 --- a/src/pydbm/database/manager.py +++ b/src/pydbm/database/manager.py @@ -171,6 +171,12 @@ def create(self, **kwargs) -> DbmModel: return model + def get_or_create(self, **kwargs) -> tuple[DbmModel, bool]: + try: + return self.get(**kwargs), False + except self.model.DoesNotExists: + return self.create(**kwargs), True + def get(self, *, id: str | None = None, **unique_together) -> DbmModel: if id is None: if self.model._config.unique_together != tuple(unique_together.keys()): diff --git a/tests/models/test_base.py b/tests/models/test_base.py index ae679d5..c9767c1 100644 --- a/tests/models/test_base.py +++ b/tests/models/test_base.py @@ -404,3 +404,47 @@ class Model(DbmModel): last = Model.objects.last() assert last is not None assert last.username in ("hakan", "celik") + + +def test_base_get_or_create_creates(teardown_db): + class Model(DbmModel): + username: str + + class Config: + unique_together = ("username",) + + instance, created = Model.objects.get_or_create(username="hakan") + assert created is True + assert instance.username == "hakan" + assert Model.objects.count() == 1 + + +def test_base_get_or_create_gets(teardown_db): + class Model(DbmModel): + username: str + + class Config: + unique_together = ("username",) + + Model.objects.create(username="hakan") + instance, created = Model.objects.get_or_create(username="hakan") + assert created is False + assert instance.username == "hakan" + assert Model.objects.count() == 1 + + +def test_base_get_or_create_multiple_fields(teardown_db): + class Model(DbmModel): + name: str + surname: str + + class Config: + unique_together = ("name", "surname") + + instance, created = Model.objects.get_or_create(name="hakan", surname="celik") + assert created is True + + instance2, created2 = Model.objects.get_or_create(name="hakan", surname="celik") + assert created2 is False + assert instance.id == instance2.id + assert Model.objects.count() == 1