Repository: django-extensions/django-extensions

Issue:
Cannot combine --exclude and --include with graph_models
## What I'm trying to achieve
I would like to be able to include some models through given wildcards. But yet be able to include some of them against given wildcards.

## Example
Let's say I want to only select some given models and all the models containing the word `Attribute` but yet, exclude any containing the word `Translation`. I would do the following:
```
./manage.py graph_models product \
    -I '*Attribute*,Product,ProductVariant,ProductType' \
    -X '*Translation*' \
    --pygraphviz -g -o attributes_class_tree.png
```

## What I expected to happen
1. Have the models matched against the exclude list
1. If the model didn't match, match against the include list

## What happened instead
Nothing was selected, thus nothing was rendered.

## Suggested solution
```diff
Index: django_extensions/management/modelviz.py
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- django_extensions/management/modelviz.py	(revision 52cbf38144109a705f6932dd996540b66f014494)
+++ django_extensions/management/modelviz.py	(date 1567019406336)
@@ -381,18 +381,18 @@
         Decide whether to use a model, based on the model name and the lists of
         models to exclude and include.
         """
+        # Check against include list.
+        if self.include_models:
+            for model_pattern in self.include_models:
+                model_pattern = '^%s$' % model_pattern.replace('*', '.*')
+                if re.search(model_pattern, model_name):
+                    return True
         # Check against exclude list.
         if self.exclude_models:
             for model_pattern in self.exclude_models:
                 model_pattern = '^%s$' % model_pattern.replace('*', '.*')
                 if re.search(model_pattern, model_name):
                     return False
-        # Check against exclude list.
-        elif self.include_models:
-            for model_pattern in self.include_models:
-                model_pattern = '^%s$' % model_pattern.replace('*', '.*')
-                if re.search(model_pattern, model_name):
-                    return True
         # Return `True` if `include_models` is falsey, otherwise return `False`.
         return not self.include_models
```

<p align='center'>(snippet from <a href='https://github.com/django-extensions/django-extensions/blob/d6d07584004f2a1fa0734edfc144fcf37f33ac4b/django_extensions/management/modelviz.py#L379-L397'>management/modelviz.py</a>)</p>

---

I will open a pull request later on today, with the [adequate tests](https://github.com/django-extensions/django-extensions/blob/7ac4792c72338c95d4e2865a786504b28c157383/tests/test_management_command.py#L127-L222). Let me know about what you think.


Discussion:

Resolving patch:
diff --git a/django_extensions/management/modelviz.py b/django_extensions/management/modelviz.py
--- a/django_extensions/management/modelviz.py
+++ b/django_extensions/management/modelviz.py
@@ -381,18 +381,18 @@ def use_model(self, model_name):
         Decide whether to use a model, based on the model name and the lists of
         models to exclude and include.
         """
+        # Check against include list.
+        if self.include_models:
+            for model_pattern in self.include_models:
+                model_pattern = '^%s$' % model_pattern.replace('*', '.*')
+                if re.search(model_pattern, model_name):
+                    return True
         # Check against exclude list.
         if self.exclude_models:
             for model_pattern in self.exclude_models:
                 model_pattern = '^%s$' % model_pattern.replace('*', '.*')
                 if re.search(model_pattern, model_name):
                     return False
-        # Check against exclude list.
-        elif self.include_models:
-            for model_pattern in self.include_models:
-                model_pattern = '^%s$' % model_pattern.replace('*', '.*')
-                if re.search(model_pattern, model_name):
-                    return True
         # Return `True` if `include_models` is falsey, otherwise return `False`.
         return not self.include_models
 


Test patch:
diff --git a/tests/test_management_command.py b/tests/test_management_command.py
--- a/tests/test_management_command.py
+++ b/tests/test_management_command.py
@@ -138,6 +138,7 @@ def test_use_model(self):
             '*WildcardPrefixExclude',
             'WildcardSuffixExclude*',
             '*WildcardBothExclude*',
+            '*Include',
         ]
         # Any model name should be used if neither include or exclude
         # are defined.
@@ -220,6 +221,19 @@ def test_use_model(self):
             None,
             exclude_models,
         ))
+        # Test with `exclude_models` and `include_models` combined
+        # where the user wants to exclude some models through a wildcard
+        # while still being able to include given models
+        self.assertTrue(use_model(
+            'MyWildcardPrefixInclude',
+            include_models,
+            exclude_models
+        ))
+        self.assertFalse(use_model(
+            'MyInclude',
+            include_models,
+            exclude_models
+        ))
 
     def test_no_models_dot_py(self):
         data = generate_graph_data(['testapp_with_no_models_file'])