django - 比较两个对象[动态使用字段]

时间:2015-07-08 07:37:02

标签: django django-models

我需要比较两个对象,以确定某个字段是否已更改

class Country(models.Model):  # country code 'MX' -> Mexico
    code = models.CharField(max_length=2)
    name = models.CharField(max_length=15)


class Client(models.Model): # id=1, name=pedro, country.code=MX, rfc=12345
    name = models.CharField(max_length=100)
    country = models.ForeignKey(Country)
    rfc = models.CharField(max_length=13)



> obj_db = Client.object.get(id=1)

> country = Country.objects.get(code='MX')
obj_no_db = Client(**{'id':1, 'name':'pedro', 'country': country, 'rfc':12345})

> obj_db == obj_no_db # True

> obj_no_db = Client(**{'id':1, 'name':'pedro', 'country': country, 'rfc':1})

> obj_db == obj_no_db # True # but isn't True because the rfc has change, how can compare field by field

> obj_db.rfc == obj_no_db.rfc # False I expected this result

我需要构建一个通用的功能,问题是我没有找到有关它的信息,我想我可以使用._meta选项,但我不确定。 我开发了这个功能,但我无法逐个发现比较的方法。

def get_insert_update(obj, key, obj_list, fields=None, exclude_fields=None):
    """

    :param obj: The object for compare
    :param key: a the key for compare to determine if we need to update or insert
    :param obj_list: list objects to compare
    :return: to_insert, _update
    """
    db = {}
    to_insert = []
    to_update = []
    if key == 'pk':  # the field pk doesn't exists so we change to id, because its the same
        key = 'id'
    exclude_fields = exclude_fields or []
    fields = fields or []
    if 'pk' in fields:
        fields[fields.index('pk')] = 'id'  # we change the field pk, because it doesn't exists
    if 'pk' in exclude_fields:
        exclude_fields[exclude_fields.index('pk')] = 'id'  # we change the field pk, because it doesn't exists

    meta = obj._meta  # we define meta object

    if fields is None:
        fields = meta.get_all_field_names()

    fields = [f for f in meta.fields if f.attname in fields]

    # dumping db into memory
    for _obj in obj.objects.all():
        if isinstance(key, list):  # first check if is a list to create a custom key
            _key = _get_key(_obj, key)
        else:
            _key = _obj.__dict__[key]
        # if exclude fields exists
        if exclude_fields:
            d = {f.attname: _obj.__dict__[f.attname] for f in fields if f.attname not in exclude_fields}
            db[_key] = obj(**d)
        else:  # we save the full object
            db[_key] = _obj
    # read local objects to determine if the record will be insert or update
    for _obj in obj_list:
        if isinstance(key, list):  # first check if is a list to create a custom key
            _key = _get_key(_obj, key)
        else:
            _key = _obj.__dict__[key]
        if _key in db:  # if the key is in db so we check if it equal
            # if _obj.pk == 6: # debug
            #    print(_obj.__dict__, db[_key].__dict__, _obj.__dict__ == db[_key].__dict__)
            if _obj != db[_key]: # HERE i need the determine if the fields are equal or not.
                to_update.append(_obj)  # if the object has changed, we update it
            else:
                pass  # if the object is equal, we didn't do it anything
        else:
            to_insert.append(_obj)  # because we didn't found into the database, we create it
    return to_insert, to_update

def _get_key(obj, lst):
    """
    create a string key using multiples keys 
    Example: obj.id -> 1, obj.name -> 'foo' 
    lst['id', 'name']
    :param lst: list of keys
    :return: 1_foo
    """
    k = []
    for t in lst:
        k.append(str(obj.__dict__[t]))
    return "_".split(k)

1 个答案:

答案 0 :(得分:4)

Django的Model类根据__eq__属性的值定义要比较的pk方法,这就是你的模型比较相等的原因。

执行此操作的一种简单方法是在您自己的模型上覆盖该方法,以比较__dict__的值,该值包含所有实例的值。

这有点轻微的问题,因为__dict__还包含一个隐藏的_state对象,该对象将按ID进行比较,因此您需要对其进行过滤比较。

def __eq__(self, other):
     values = [(k,v) for k,v in self.__dict__.items() if k != '_state']
     other_values = [(k,v) for k,v in other.__dict__.items() if k != '_state']
     return values == other_values