def model_to_dict(m, field_names=None): """ Convert Django Model instance to dict based on given field names. Attribute names can be re-written if the given field_names is a dict. """ if field_names == None: if hasattr(m, 'get_json_fieldnames'): field_names = m.get_json_fieldnames() else: field_names = get_model_field_names(m) obj = {} # encode fields for attr in field_names: try: v = get_model_attr(m, attr) if isinstance(field_names, list): obj[attr] = v elif isinstance(field_names, dict): obj[field_names[attr]] = v except: pass return obj
def get_model_fields(self, model): """ Return a list of all fields for the given model. Certain fields are excluded for now, for example ManyToMany fields, because we do not support them yet. """ for fieldname in get_model_field_names(model): try: field = model._meta.get_field(fieldname) except FieldDoesNotExist: continue # we do not attempt to sync arbitary relations yet, the only # field that we do support is ForeignKey if isinstance(field, RelatedField) and not \ isinstance(field, models.ForeignKey): continue yield field
def get_changes(self, a, b=None): """ Return a structure in JSON that describes the changes that occurred for the given model a (before the change) and b (after the change). """ # nothing to work with if a is None and b is None: return None # identify model class and get list of known models _class = a.__class__ models = get_models() # identify all database columns from model fieldnames = get_model_field_names(_class) # generate index over b (if available) index = {} if b is not None: for entry in b: index[entry.get('n')] = entry.get('a') def _array_equal(ass, bss): if len(ass) != len(bss): return False for i, a in enumerate(ass): if a != bss[i]: return False return True # generate changes result = [] for fieldname in fieldnames: # get field is model field = _class._meta.get_field(fieldname) # ignore file fields if isinstance(field, DjangoFileField): continue # get previous value (b) if b is not None: vb = index.get(field.attname) else: vb = None # get current and previous values if field.many_to_many: # ignore ManyToMany with through model. It will be picked up # by collecting related objects has_through_model = False for m in models: if issubclass(field.rel.through, m): has_through_model = True break if has_through_model: continue # get list of pk's for many-to-many related objects if a.pk: va = getattr(a, field.name) if va is not None: va = [x.pk for x in va.all()] else: va = [] else: va = getattr(a, field.attname) # value changed? changed = False if b is not None: if isinstance(va, list) and isinstance(vb, list): changed = not _array_equal(va, vb) else: changed = va != vb if b is None or changed: result.append({'n': field.attname, 'a': va, 'b': vb}) return result
def test_get_model_field_names_should_call_get_json_fieldnames_to_determine_json_properties(self): self.assertEqual( ['id', 'title'], get_model_field_names(TestModelWithJsonFields(), json=True) )
def _get_fields_for_empty_model_json(): get_model_field_names('', json=True)
def _get_fields_for_empty_model(): get_model_field_names('')
def test_get_model_field_names_should_return_correct_number_of_fields(self): number_of_fields = len(get_model_field_names(TestModel)) number_of_fields_json = len(get_model_field_names(TestModel, json=True)) self.assertEqual(number_of_fields, 11) self.assertEqual(number_of_fields_json, 11)
def test_get_model_field_names_should_return_list(self): self.assertIsInstance(get_model_field_names(TestModel), list, 'must be list') self.assertIsInstance(get_model_field_names(TestModel, json=True), list, 'must be list')