Repository: pydantic/pydantic

Issue:
Allow partial parametrization of Generic classes
### Initial Checks

- [X] I confirm that I'm using Pydantic V2

### Description

I believe this issue might be linked to #6980 and probably I am missing something, but I really do not get why the code below throws a TypeError:

```
TypeError: All parameters must be present on typing.Generic; you should inherit from typing.Generic[~KE, ~KD, ~E, ~D, ~Q] (bases=(<class '__main__.BaseGenericClass'>,))
```

I would expect that, as it happens for mypy ***and with pydantic<2.0.0***, having fixed to Dict[str,Any] and str the type variables D and Q in the ChildGenericClass it would become a Generic[KE, KD, E] and throws no error.

Can anyone explain me what I am missing (and maybe how to avoid this situation)?

### Example Code

```Python
from pydantic import BaseModel
from typing import Any, Dict, Generic, TypeVar

KE = TypeVar("KE") 
KD = TypeVar("KD")
E = TypeVar("E", bound=BaseModel)
D = TypeVar("D")
Q = TypeVar("Q")

class BaseGenericClass(Generic[KE, KD, E, D, Q], BaseModel):
    uid: str
    name: str
    
class ChildGenericClass(
    BaseGenericClass[KE, KD, E, Dict[str, Any], str],
):
    ...
```


### Python, Pydantic & OS Version

```Text
Python: 3.8.16
Pydantic: 2.1.1
OS: MacOS Ventura 13.4.1
```


Selected Assignee: @lig


Discussion:
@NicolaDonelli thank you for the request. Currently Pydantic v2 requires full parametrization of a Generic models. Partial parametrization isn't supported yet.

This looks like a legitimate feature request to me. A PR solving this would be highly appreciated.
Putting the `Generic` _after_ `BaseModel` in the class declaration fixes this issue, as far as I can tell:
```python
from pydantic import BaseModel
from typing import Any, Dict, Generic, TypeVar

KE = TypeVar("KE") 
KD = TypeVar("KD")
E = TypeVar("E", bound=BaseModel)
D = TypeVar("D")
Q = TypeVar("Q")

class BaseGenericClass(BaseModel, Generic[KE, KD, E, D, Q]):
    uid: str
    name: str
    
class ChildGenericClass(
    BaseGenericClass[KE, KD, E, Dict[str, Any], str],
):
    ...
```
Is there a reason that you'd want to put `Generic` first? Otherwise, I would suggest that we just make a clearer error message if you try putting `Generic` before `BaseModel`.
Also, I'll just note that I believe this is a separate issue unrelated to #6980.

Resolving patch:
diff --git a/pydantic/_internal/_model_construction.py b/pydantic/_internal/_model_construction.py
--- a/pydantic/_internal/_model_construction.py
+++ b/pydantic/_internal/_model_construction.py
@@ -138,15 +138,20 @@ def wrapped_model_post_init(self: BaseModel, __context: Any) -> None:
                 if parameters and parent_parameters and not all(x in parameters for x in parent_parameters):
                     combined_parameters = parent_parameters + tuple(x for x in parameters if x not in parent_parameters)
                     parameters_str = ', '.join([str(x) for x in combined_parameters])
+                    generic_type_label = f'typing.Generic[{parameters_str}]'
                     error_message = (
                         f'All parameters must be present on typing.Generic;'
-                        f' you should inherit from typing.Generic[{parameters_str}]'
+                        f' you should inherit from {generic_type_label}.'
                     )
                     if Generic not in bases:  # pragma: no cover
-                        # This branch will only be hit if I have misunderstood how `__parameters__` works.
-                        # If that is the case, and a user hits this, I could imagine it being very helpful
-                        # to have this extra detail in the reported traceback.
-                        error_message += f' (bases={bases})'
+                        # We raise an error here not because it is desirable, but because some cases are mishandled.
+                        # It would be nice to remove this error and still have things behave as expected, it's just
+                        # challenging because we are using a custom `__class_getitem__` to parametrize generic models,
+                        # and not returning a typing._GenericAlias from it.
+                        bases_str = ', '.join([x.__name__ for x in bases] + [generic_type_label])
+                        error_message += (
+                            f' Note: `typing.Generic` must go last: `class {cls.__name__}({bases_str}): ...`)'
+                        )
                     raise TypeError(error_message)
 
                 cls.__pydantic_generic_metadata__ = {


Test patch:
diff --git a/tests/test_generics.py b/tests/test_generics.py
--- a/tests/test_generics.py
+++ b/tests/test_generics.py
@@ -2022,6 +2022,27 @@ class B(A[T], Generic[S]):
             ...
 
 
+def test_generic_subclass_with_extra_type_with_hint_message():
+    E = TypeVar('E', bound=BaseModel)
+    D = TypeVar('D')
+
+    class BaseGenericClass(Generic[E, D], BaseModel):
+        uid: str
+        name: str
+
+    with pytest.raises(
+        TypeError,
+        match=re.escape(
+            'All parameters must be present on typing.Generic; you should inherit from typing.Generic[~E, ~D].'
+            ' Note: `typing.Generic` must go last:'
+            ' `class ChildGenericClass(BaseGenericClass, typing.Generic[~E, ~D]): ...`'
+        ),
+    ):
+
+        class ChildGenericClass(BaseGenericClass[E, Dict[str, Any]]):
+            ...
+
+
 def test_multi_inheritance_generic_binding():
     T = TypeVar('T')