diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..5f9c639 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,36 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +(Note: this should be a complete and concise piece of code that allows reproduction of an issue) + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Versions (please complete the following information):** + - Database backend used (mysql/sqlite/postgress) + - Python version + - `ormar` version + - `pydantic` version + - if applicable `fastapi` version + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..73e70e0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,7 @@ +contact_links: + - name: I have a question ❓ + url: https://github.com/collerek/ormar/discussions + about: If you have any question about the usage of ormar, please open a discussion first. + - name: I want a new feature 🆕 + url: https://github.com/collerek/ormar/discussions + about: If you would like to request or make a change/enhancement that is not trivial, please open a discussion first. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..11fc491 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: enhancement +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/docs/releases.md b/docs/releases.md index 4b5a94e..30e6fcb 100644 --- a/docs/releases.md +++ b/docs/releases.md @@ -1,3 +1,9 @@ +# 0.9.4 + +## Fixes +* Fix `fastapi` OpenAPI schema generation for automatic docs when multiple models refer to the same related one + + # 0.9.3 ## Fixes diff --git a/ormar/__init__.py b/ormar/__init__.py index 4ef50dd..b2c7020 100644 --- a/ormar/__init__.py +++ b/ormar/__init__.py @@ -68,7 +68,7 @@ class UndefinedType: # pragma no cover Undefined = UndefinedType() -__version__ = "0.9.3" +__version__ = "0.9.4" __all__ = [ "Integer", "BigInteger", diff --git a/ormar/fields/foreign_key.py b/ormar/fields/foreign_key.py index 16bb23d..f6bccfa 100644 --- a/ormar/fields/foreign_key.py +++ b/ormar/fields/foreign_key.py @@ -1,6 +1,8 @@ +import string import sys import uuid from dataclasses import dataclass +from random import choices from typing import Any, List, Optional, TYPE_CHECKING, Tuple, Type, Union import sqlalchemy @@ -67,9 +69,14 @@ def create_dummy_model( :return: constructed dummy model :rtype: pydantic.BaseModel """ + alias = ( + "".join(choices(string.ascii_uppercase, k=2)) + uuid.uuid4().hex[:4] + ).lower() fields = {f"{pk_field.name}": (pk_field.__type__, None)} dummy_model = create_model( - f"PkOnly{base_model.get_name(lower=False)}", **fields # type: ignore + f"PkOnly{base_model.get_name(lower=False)}{alias}", + __module__=base_model.__module__, + **fields, # type: ignore ) return dummy_model diff --git a/tests/test_docs_with_multiple_relations_to_one.py b/tests/test_docs_with_multiple_relations_to_one.py new file mode 100644 index 0000000..6baffa6 --- /dev/null +++ b/tests/test_docs_with_multiple_relations_to_one.py @@ -0,0 +1,72 @@ +from typing import Optional +from uuid import UUID, uuid4 + +import databases +import sqlalchemy +from fastapi import FastAPI +from starlette.testclient import TestClient + +import ormar + +app = FastAPI() +DATABASE_URL = "sqlite:///db.sqlite" +database = databases.Database(DATABASE_URL) +metadata = sqlalchemy.MetaData() + + +class BaseMeta(ormar.ModelMeta): + metadata = metadata + database = database + + +class CA(ormar.Model): + class Meta(BaseMeta): + tablename = "cas" + + id: UUID = ormar.UUID(primary_key=True, default=uuid4) + ca_name: str = ormar.Text(default="") + + +class CB1(ormar.Model): + class Meta(BaseMeta): + tablename = "cb1s" + + id: UUID = ormar.UUID(primary_key=True, default=uuid4) + cb1_name: str = ormar.Text(default="") + ca1: Optional[CA] = ormar.ForeignKey(CA, nullable=True) + + +class CB2(ormar.Model): + class Meta(BaseMeta): + tablename = "cb2s" + + id: UUID = ormar.UUID(primary_key=True, default=uuid4) + cb2_name: str = ormar.Text(default="") + ca2: Optional[CA] = ormar.ForeignKey(CA, nullable=True) + + +@app.get("/ca", response_model=CA) +async def get_ca(): # pragma: no cover + return None + + +@app.get("/cb1", response_model=CB1) +async def get_cb1(): # pragma: no cover + return None + + +@app.get("/cb2", response_model=CB2) +async def get_cb2(): # pragma: no cover + return None + + +def test_all_endpoints(): + client = TestClient(app) + with client as client: + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + schema = response.json() + components = schema["components"]["schemas"] + assert all(x in components for x in ["CA", "CB1", "CB2"]) + pk_onlys = [x for x in list(components.keys()) if x.startswith("PkOnly")] + assert len(pk_onlys) == 2