Skip to content

Instantly share code, notes, and snippets.

@foxx
Created November 27, 2014 00:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save foxx/1968fe19f759e608452a to your computer and use it in GitHub Desktop.
Save foxx/1968fe19f759e608452a to your computer and use it in GitHub Desktop.
Django partial forms mixin (alpha as fuck)
class FormFieldQuerySetMixin(object):
"""
Adds support for changing RelatedField queryset
easily without having to patch __init__.
def get_FIELDNAME_queryset(self):
pass
"""
def __init__(self, *args, **kwargs):
super(FormFieldQuerySetMixin, self).__init__(*args, **kwargs)
assert isinstance(self, (ModelForm, ))
for field in self.fields.keys():
fn = "get_%s_queryset" % ( field, )
func = getattr(self, fn, None)
if func:
qs = func()
old_qs = self.fields[field].queryset
assert qs.model == old_qs.model
self.fields[field].queryset = qs
class CreateOnlyFormFieldMixin(object):
"""
Adds create-only field support into a model form, this
allows a field to be written at creation but not updated
"""
def __init__(self, *args, **kwargs):
super(CreateOnlyFormFieldMixin, self).__init__(*args, **kwargs)
for field_name in self.get_fields_createonly():
assert field_name in self.fields, "invalid create-only field '%s'" % ( field_name, )
if self.instance and self.instance.pk:
setattr(self.fields[field_name], '_createonly', True)
self.fields[field_name].widget.attrs['readonly'] = True
def _clean_fields(self):
for name, field in self.fields.items():
is_createonly = getattr(field, '_createonly', False)
if field in self.data and is_createonly:
self.add_error(field, "Field is create-only")
return
super(CreateOnlyFormFieldMixin, self)._clean_fields()
@classmethod
def get_fields_createonly(self):
fields_createonly = getattr(self.Meta, 'fields_createonly', None)
fields_createonly = fields_createonly if fields_createonly else []
return fields_createonly
class ReadOnlyFormFieldMixin(object):
"""
Adds read-only field support into form, allows fields to be
added dynamically, with inspiration from;
http://stackoverflow.com/questions/324477
http://stackoverflow.com/questions/972
"""
def __init__(self, *args, **kwargs):
super(ReadOnlyFormFieldMixin, self).__init__(*args, **kwargs)
for field_name in self.get_fields_readonly():
assert field_name in self.fields, "invalid read-only field '%s'" % ( field_name, )
setattr(self.fields[field_name], '_readonly', True)
self.fields[field_name].widget.attrs['readonly'] = True
self.fields[field_name].editable = False
def _clean_fields(self):
for name, field in self.fields.items():
is_readonly = getattr(field, '_readonly', False)
if field in self.data and is_readonly:
self.add_error(field, "Field is read-only")
return
super(ReadOnlyFormFieldMixin, self)._clean_fields()
@classmethod
def get_fields_readonly(self):
fields_readonly = getattr(self.Meta, 'fields_readonly', None)
fields_readonly = fields_readonly if fields_readonly else []
return fields_readonly
class WriteOnlyFormFieldMixin(object):
"""
Adds support for hiding instance value if field
is write only
"""
def __init__(self, *args, **kwargs):
super(WriteOnlyFormFieldMixin, self).__init__(*args, **kwargs)
for field_name in self.get_fields_writeonly():
assert field_name in self.fields, "invalid write-only field '%s'" % ( field_name, )
setattr(self.fields[field_name], '_writeonly', True)
if field_name in self.initial:
del self.initial[field_name]
@classmethod
def get_fields_writeonly(self):
fields_writeonly = getattr(self.Meta, 'fields_writeonly', None)
fields_writeonly = fields_writeonly if fields_writeonly else []
return fields_writeonly
class PartialFormMixin(object):
"""
Adds partial form support which makes all fields optional
if form is created with partial attribute;
form = MyForm(data, partial=True)
"""
partial = False
def __init__(self, *args, **kwargs):
if 'partial' in kwargs:
self.partial = kwargs['partial']
del kwargs['partial']
super(PartialFormMixin, self).__init__(*args, **kwargs)
if self.partial:
for field in self.fields:
self.fields[field].required = False
def _post_clean(self):
if self.partial:
cleaned_data = dict([ (k,v) for (k,v) in self.cleaned_data.items()
if k in self.data ])
self.cleaned_data = cleaned_data
super(PartialFormMixin, self)._post_clean()
@collinanderson
Copy link

Hi, You could probably simplify PartialFormMixin to something like this:

class PartialFormMixin(object):
    def __init__(self, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        super(PartialFormMixin, self).__init__(*args, **kwargs)
        if partial:
            for name in list(self.fields):
                if name not in self.data:
                    del self.fields[name]

@foxx
Copy link
Author

foxx commented Jan 31, 2015

@collinanderson Doh, github didn't tell me that I had unread comments, just stumbled across this now. You might be right, I'll give it a try and update if it works, thanks :)

@anentropic
Copy link

@collinanderson ...works a treat

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment