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

Callable schemas and initial values #85

Open
corradio opened this issue Dec 17, 2022 · 4 comments
Open

Callable schemas and initial values #85

corradio opened this issue Dec 17, 2022 · 4 comments
Labels
enhancement New feature or request

Comments

@corradio
Copy link

Hello,

It seems that callable schemas don't have knowledge of initial values (e.g. using a CreateView), as the schema callable only receives the model instance as parameter, which is not initialised.
For example, using a model MyModel having a JSONField with a callable schema based on the model's some_property attribute, and using this view

class MetricCreateView(CreateView, LoginRequiredMixin):
    model = MyModel
    form_class = MyModelForm
    def get_initial(self):
        return {'some_property': self.kwargs['some_property']}

the callable schema will not be able to return a schema based on the initial some_property value.
Wouldn't it make sense to pass the initial values to the callable? They are passed to the kwargs of MyModelForm.

@bhch
Copy link
Owner

bhch commented Dec 17, 2022

Hi, I think I kind of understand what you're proposing.

But if you could also provide a minimal example of the model and your callable schema function, it will be a bit clearer to me.

@corradio
Copy link
Author

Hello,
I've given a more complete example below, alongside my current (hacky) solution. I hope this clarifies things.

def schema_getter(some_property):
    # .... <fetches schema based on value of `some_property`>

# Model
class MyModel(models.Model):
    some_property = models.CharField(max_length=128)
    def callable_config_schema(model_instance=None):
        # Here it would be great to be able to know what the initial value of
        # `some_property` is, as the model instance hasn't been instantiated yet
        # for CreateView
        if model_instance: return schema_getter(model_instance.some_property)
    some_json = JSONField(schema=callable_config_schema)

# Form
class MyModelForm(ModelForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # This is my hack to make sure the schema is correctly selected
        if kwargs.get("initial", {}).get("some_property"):
            self.fields["some_json"].widget.schema = self.fields[
                "some_json"
            ].widget.schema(MyModel(integration_id=kwargs["initial"]["some_property"]))
        # manually set the current instance on the widget
        # see https://django-jsonform.readthedocs.io/en/latest/fields-and-widgets.html#accessing-model-instance-in-callable-schema
        self.fields["some_json"].widget.instance = self.instance
    class Meta:
        model = MyModel
        fields = "__all__"

# View
class MyCreateView(CreateView):
    model = MyModel
    form_class = MyModelForm
    def get_initial(self):
        # self.kwargs contains parameters from the url
        return {'some_property': self.kwargs['some_property']}

In urls.py:

urlpatterns = [
    path(
        "some/path/<some_property>/",
        views.MyCreateView.as_view()
    ),
    ....

@bhch
Copy link
Owner

bhch commented Dec 17, 2022

Okay, everything is clear now. I will provide a way to pass extra arguments to the callable in the next release. Probably introduce a new attribute called schema_kwargs. Something like this:

# declare extra arguments to pass to the callable schema
self.fields['some_json'].widget.schema_kwargs = {'key': value, ...}

Meanwhile, let me suggest a simpler hack for your use case.

The widget doesn't care what the value of the instance attribute is. You can set it to whatever value you want and it will be passed to the callable, i.e. you can treat it as a vehicle to carry any kind of data:

# form
class MyModelForm(ModelForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.fields["some_json"].widget.instance = {
            'instance': self.instance,
            'some_property': kwargs['initial']['some_property']
        }


# callable
def callable_config_schema(data):
    """
    data is a dict containing these keys:
        - instance: instance of the model
        - some_property: value of some_property field
    """"
    if data['instance'] is not None:
        # model instance is present
       ...
    else:
        # no instance found, so use data['some_property'] value
        ... 

@bhch bhch added the enhancement New feature or request label Dec 17, 2022
@corradio
Copy link
Author

Good idea - thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants