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

5

我需要比较两个对象,以确定一个字段是否发生了变化

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个回答

11

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

1
它帮助我理解为什么对象之间的比较总是为真。如果您不介意,我可以在哪里找到更多相关信息? - paridin
非常感谢!这是一个很棒的解决方案,甚至可以根据选择的属性比较项目。 - Alex

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接