django model新增/更新数据

本文介绍了如何在Django中使用Model进行数据的新增和更新。在新增数据时,可以通过重写save()方法添加自定义数据处理。对于更新数据,文章提到了一篇掘金上详细讲解的文章链接。此外,还提及了使用原生SQL语句进行数据库操作的可能性,并引用了他人代码作为参考。
django model:
class Poll(models.Model):
    name= models.CharField(max_length=255, unique=True, verbose_name='name')
    description = models.CharField(max_length=255, unique=True, verbose_name='description')
新增数据:
data_dict = {'name': 'foo', 'description': 'bar'}
# 1
p = Poll(**data_dict)
p.save()
# 2
p = Poll(name='foo', description='bar')
p.save()
# 3
Poll.objects.create(name='foo', description='bar')
# 4
Poll.objects.create(**data_dict)

另外,可以重写Poll模块的save()方法,增加自定义数据处理,处理完调用super().save()

更新数据:

参考掘金上面这位同学的文章,写的很清晰:https://juejin.im/post/5b588b656fb9a04fba6e8681#heading-0

附注:
当然数据库的新增和更新操作也可以使用原生的sql语句, 摘段别人的代码记录一下(https://blog.youkuaiyun.com/You_are_my_dream/article/details/53037680)

from django.db import connection
cursor = connection.cursor()
cursor.execute("insert into blog_author(name) values('韩寒')")
cursor.execute("update blog_author set name = '郭敬明' where name = '韩寒' ")
cursor.execute("delete from blog_author where name = '郭敬明'")
cursor.execute('select * from blog_author')
# 一条一条的取数据
raw = cursor.fetchone()
# 取出所有数据,以元组的形式返
cursor.fetchall()
<think> 我们参考了多个引用,特别是引用[3]中提到了删除重复数据更新模型为unique的方法。用户的问题是如何在Django模型中去除ID重复的数据。注意,这里用户说的是“ID重复”,但通常ID是主键,不会重复。我们怀疑用户可能是指其他字段重复,而ID不同。因此,我们需要明确:如果用户确实是指主键重复,这是不应该发生的,因为主键必须唯一。但更可能的是,用户是指某个字段(比如某个唯一标识字段)重复,而需要删除重复的记录(保留一条)。 根据引用[3]中的示例,我们可以使用以下步骤: 1. 首先找出重复的数据(基于某个字段,比如access_key_id),然后对于每个重复的键值,保留一条记录(比如第一条),删除其他重复记录。 但是用户的问题中提到了“ID重复”,如果真的是主键重复,那么这通常是由于数据库错误引起的,非常罕见。我们假设用户可能是笔误,实际上是想删除某个字段重复的数据(例如,某个业务字段重复,而需要去重)。 因此,我们给出两种可能的情况: 情况1:如果是主键重复(这种情况极少出现,因为主键自动唯一),那么需要先检查数据库的完整性,然后手动处理(因为Django ORM不允许主键重复)。 情况2:更常见的,用户是想根据某个字段(或多个字段组合)去重,删除重复的记录(保留一条)。 我们按照情况2来回答,因为情况1需要直接操作数据库,不是ORM的常规操作。 参考引用[3]的方法,我们可以这样做: 步骤: 1. 使用values()和annotate()来找出重复的字段组合,并计数。 2. 遍历这些重复的字段组合,对每个组合,取出所有记录,然后保留一条(例如第一条),删除其他。 注意:在删除时,我们通常保留第一个(按某个顺序,比如主键升序),但用户也可以根据需要保留最后一条或其他。 示例代码(假设模型为MyModel,要去重的字段为'field1','field2'): ```python from django.db.models import Count # 找出重复的数据(按field1和field2分组,并计数) duplicates = MyModel.objects.values('field1', 'field2').annotate(count=Count('id')).filter(count__gt=1) for duplicate in duplicates: # 对于每个重复的字段组合,取出所有记录 records = MyModel.objects.filter(field1=duplicate['field1'], field2=duplicate['field2']) # 我们按id排序,保留第一个(id最小的),删除其他 # 注意:这里不能使用records[1:]直接删除,因为可能重复超过2条 # 我们跳过第一个,删除后面的所有 records_to_delete = records.order_by('id')[1:] # 从第二个开始 for record in records_to_delete: record.delete() ``` 然而,上面的方法在重复记录很多时,可能会比较慢,因为每条记录都单独删除。我们可以使用批量删除: ```python for duplicate in duplicates: records = MyModel.objects.filter(field1=duplicate['field1'], field2=duplicate['field2']) # 获取要保留的第一个记录的主键 keep_id = records.order_by('id').values_list('id', flat=True).first() # 删除该重复组中除了要保留的那条记录以外的所有记录 records.exclude(id=keep_id).delete() ``` 注意:这种方法在重复组很多时,会执行多个delete查询(每个重复组一个delete)。如果重复组很多,可能效率不高。 另一种方法(使用一个查询删除所有重复记录,但保留每个组的最小id): 这种方法需要使用Django的F表达式和子查询,但是Django ORM不支持在删除时直接使用窗口函数。我们可以使用两个查询集: 首先,我们找到每个重复组的最小id(保留的id): ```python from django.db.models import Min, Count # 找出每个重复组的最小id(按field1,field2分组) min_ids = MyModel.objects.values('field1','field2').annotate(min_id=Min('id')) # 然后,我们构造一个查询集,排除这些最小id的记录,并且是重复组中的记录(注意:这里我们只删除重复组中的记录,非重复组不动) # 但是,我们如何标记重复组?我们可以先找出所有重复的组(即出现大于1次的组) duplicate_groups = MyModel.objects.values('field1','field2').annotate(count=Count('id')).filter(count__gt=1) # 然后,我们删除那些在重复组中且id不在min_ids中的记录 # 注意:min_ids是一个ValuesQuerySet,每个元素是一个字典,包含field1,field2,min_id # 但是,我们无法直接用min_ids来构建一个排除列表(因为每个组的最小id不同) # 我们可以这样: # 获取所有要保留的最小id的列表(每个组一个) keep_ids = [group['min_id'] for group in min_ids] # 然后,我们要删除的是:在重复组中,并且id不在keep_ids中的记录 # 首先,我们获取重复组中所有记录的id # 但是,重复组已经被我们查询出来了(duplicate_groups),我们可以构建一个条件:Q对象,条件为field1和field2在重复组中,且id不在keep_ids中 # 但是,由于duplicate_groups是一个字典列表,我们可以这样构建条件: from django.db.models import Q # 创建一个空的Q对象 conditions = Q() for group in duplicate_groups: # 每个group是一个字典,有field1和field2 conditions |= Q(field1=group['field1'], field2=group['field2']) # 现在,conditions表示所有重复组中的记录 # 然后,我们排除那些要保留的id(即keep_ids) MyModel.objects.filter(conditions).exclude(id__in=keep_ids).delete() ``` 但是,这种方法要注意:如果重复组很多,构造的Q对象可能会很大,导致查询过于复杂。因此,我们可能更倾向于第一种方法(循环每个重复组,然后删除该组中除了最小id以外的记录)。 另外,引用[3]中使用了类似第一种方法的方式,但它是保留第一条(按顺序),删除后面的。我们参考它的写法: ```python duplicate_keys = MyModel.objects.values('field1','field2').annotate(count=Count('id')).filter(count__gt=1) for key in duplicate_keys: records = MyModel.objects.filter(field1=key['field1'], field2=key['field2']) for i, record in enumerate(records): if i > 0: # 保留第一个,删除后面的 record.delete() ``` 但是,注意这里enumerate的顺序是不确定的,所以我们应该在查询时指定排序(例如按id排序): ```python duplicate_keys = MyModel.objects.values('field1','field2').annotate(count=Count('id')).filter(count__gt=1) for key in duplicate_keys: records = MyModel.objects.filter(field1=key['field1'], field2=key['field2']).order_by('id') # 现在records按id升序排列 for i, record in enumerate(records): if i == 0: continue # 跳过第一个 record.delete() ``` 或者更高效地(避免循环每条记录): ```python for key in duplicate_keys: records = MyModel.objects.filter(field1=key['field1'], field2=key['field2']).order_by('id') # 获取第一个记录的id keep_id = records[0].id # 删除该组中除了第一个以外的记录 records.exclude(id=keep_id).delete() ``` 总结:我们推荐使用第二种方式(在每个重复组内,保留id最小的记录,删除其他)。 但是,如果重复的数据量非常大(比如上百万),这种方法可能会比较慢,因为需要先查询出所有重复组,然后对每个组执行一次删除。在这种情况下,可能需要使用数据库的窗口函数(比如row_number)来标记重复行,然后一次性删除。但Django ORM不支持窗口函数的删除操作,所以可能需要执行原始SQL。 不过,用户的问题是使用Django模型的方法,所以我们提供ORM的解决方案。 注意:在执行删除操作之前,务必先备份数据! 因此,针对用户的问题“Django model中去除ID重复数据的方法”,我们假设用户是指根据其他字段去重(保留一个),那么代码可以这样写(以模型Gerrit_Reviewer_User为例,假设根据user_id, gtrevuser_id, type_user三个字段去重): ```python from django.db.models import Count # 获取重复的字段组合 duplicates = Gerrit_Reviewer_User.objects.values('user_id', 'gtrevuser_id', 'type_user').annotate(count=Count('id')).filter(count__gt=1) for duplicate in duplicates: # 对于每个重复组,按id排序,保留第一个,删除其他 records = Gerrit_Reviewer_User.objects.filter( user_id=duplicate['user_id'], gtrevuser_id=duplicate['gtrevuser_id'], type_user=duplicate['type_user'] ).order_by('id') # 按id升序,保留最小的id # 跳过第一个,删除后面的所有 records.exclude(id=records[0].id).delete() ``` 注意:如果重复组很多,且每个组内记录数很大,这种方法可能效率较低,但通常重复组不会太多。如果确实很大,可以考虑使用批处理或者直接写SQL。 另外,引用[1]中有一段代码是删除重复数据的,但它是通过原始SQL查询重复,然后删除。我们也可以参考,但是我们这里使用ORM方式。 最后,我们强调:在删除数据之前,务必先测试(可以先打印出要删除的记录而不实际删除)并备份数据。 如果用户真的想删除主键重复的记录(这种情况极少),那么我们可以这样(但通常主键是唯一的,不应该重复): ```python # 假设主键字段为'id' duplicates = Gerrit_Reviewer_User.objects.values('id').annotate(count=Count('id')).filter(count__gt=1) # 这种情况几乎不会发生,因为主键是唯一的,所以duplicates应该是空的 ``` 因此,我们推断用户应该是想根据其他字段去重。 综上所述,我们给出基于业务字段去重的方法。 </think> ### Django 模型中去除重复数据的方法 在 Django 中去除重复数据(非主键ID重复)的方法主要基于字段组合的去重操作。以下是几种高效可靠的方法,结合引用内容和最佳实践: #### 方法1:使用聚合查询批量删除(推荐) ```python from django.db.models import Count # 1. 找出重复字段组合(示例字段:field1,field2) duplicates = YourModel.objects.values('field1', 'field2') \ .annotate(count=Count('id')) \ .filter(count__gt=1) # 2. 遍历重复组保留第一条,删除其余 for duplicate in duplicates: records = YourModel.objects.filter( field1=duplicate['field1'], field2=duplicate['field2'] ).order_by('id') # 按ID排序保留最早记录 # 跳过第一条,删除重复项 [^3] records.exclude(id=records.first().id).delete() ``` #### 方法2:使用原始SQL高效处理(大数据量适用) ```python # 直接执行SQL删除重复数据(保留最小ID的记录) from django.db import connection with connection.cursor() as cursor: cursor.execute(""" DELETE FROM your_table WHERE id NOT IN ( SELECT MIN(id) FROM your_table GROUP BY field1, field2 -- 按需替换字段 ) """) ``` #### 方法3:利用`bulk_create`更新唯一约束(预防性方案) ```python # 先在模型中添加唯一约束(迁移后执行) class YourModel(models.Model): field1 = models.CharField(max_length=100) field2 = models.CharField(max_length=100) class Meta: constraints = [ models.UniqueConstraint( fields=['field1', 'field2'], name='unique_fields' ) ] # 后续插入会自动拒绝重复数据 ``` ### 关键说明 1. **主键ID不会重复**:Django默认使用自增主键`id`字段,本身具有唯一性 2. **业务字段去重**:实际需要根据业务逻辑字段(如用户名、邮箱等组合)判断重复 3. **数据安全**: - 操作前务必备份数据 (`manage.py dumpdata`) - 生产环境建议在事务中执行[^2] ```python from django.db import transaction with transaction.atomic(): # 删除操作代码 ``` ### 最佳实践建议 1. **添加数据库约束**:在模型中设置`UniqueConstraint`防止新增重复数据[^3] 2. **使用索引优化**:为去重字段创建联合索引 ```python class Meta: indexes = [ models.Index(fields=['field1', 'field2']) ] ``` 3. **替代删除方案**:对历史重复数据添加`is_active`标记而非物理删除 > **注意**:引用[1]中的代码存在安全隐患(直接拼接SQL),应改用参数化查询或ORM操作避免SQL注入风险[^5]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值