Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enable defining the model in the Resource meta via string #1669

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Please refer to :doc:`release notes<release_notes>`.
- Refactored test_admin_integration: split into smaller test modules (#1662)
- Refactored test_resources: split into smaller test modules (#1672)
- Fix deprecated ``log_action`` method (#1673)
- Enable defining Resource model as a string (#1669)

4.0.0-alpha.5 (2023-09-22)
--------------------------
Expand Down
2 changes: 1 addition & 1 deletion docs/getting_started.rst
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ describe how this resource can be imported or exported::
class BookResource(resources.ModelResource):

class Meta:
model = Book
model = Book # or 'core.Book'

Importing data
==============
Expand Down
38 changes: 20 additions & 18 deletions import_export/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import tablib
from diff_match_patch import diff_match_patch
from django.apps import apps
from django.conf import settings
from django.core.exceptions import (
FieldDoesNotExist,
Expand Down Expand Up @@ -58,8 +59,8 @@ class ResourceOptions:

model = None
"""
Django Model class. It is used to introspect available
fields.
Django Model class or full application label string. It is used to introspect
available fields.

"""
fields = None
Expand Down Expand Up @@ -250,6 +251,20 @@ class ResourceOptions:

class DeclarativeMetaclass(type):
def __new__(cls, name, bases, attrs):
def _load_meta_options(base_, meta_):
options = getattr(base_, "Meta", None)

for option in [
option
for option in dir(options)
if not option.startswith("_") and hasattr(options, option)
]:
option_value = getattr(options, option)
if option == "model" and isinstance(option_value, str):
option_value = apps.get_model(option_value)

setattr(meta_, option, option_value)

declared_fields = []
meta = ResourceOptions()

Expand All @@ -260,13 +275,7 @@ def __new__(cls, name, bases, attrs):
if hasattr(base, "fields"):
declared_fields = list(base.fields.items()) + declared_fields
# Collect the Meta options
options = getattr(base, "Meta", None)
for option in [
option
for option in dir(options)
if not option.startswith("_") and hasattr(options, option)
]:
setattr(meta, option, getattr(options, option))
_load_meta_options(base, meta)

# Add direct fields
for field_name, obj in attrs.copy().items():
Expand All @@ -278,15 +287,8 @@ def __new__(cls, name, bases, attrs):

attrs["fields"] = OrderedDict(declared_fields)
new_class = super().__new__(cls, name, bases, attrs)

# Add direct options
options = getattr(new_class, "Meta", None)
for option in [
option
for option in dir(options)
if not option.startswith("_") and hasattr(options, option)
]:
setattr(meta, option, getattr(options, option))
# add direct fields
_load_meta_options(new_class, meta)
new_class._meta = meta

return new_class
Expand Down
12 changes: 12 additions & 0 deletions tests/core/tests/test_resources/test_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -1715,3 +1715,15 @@ def test_sets_json_data_when_model_field_is_empty(self):

self.book.refresh_from_db()
self.assertEqual(self.book.data, self.data)


class BookResourceWithStringModelTest(TestCase):
def setUp(self):
class BookResourceWithStringModel(resources.ModelResource):
class Meta:
model = "core.Book"

self.resource = BookResourceWithStringModel()

def test_resource_gets_correct_model_from_string(self):
self.assertEqual(self.resource._meta.model, Book)