【问题标题】:How to convert form field data before saving to database in a Django ModelForm?如何在 Django ModelForm 中保存到数据库之前转换表单字段数据?
【发布时间】:2019-10-15 16:01:43
【问题描述】:

我正在 Django python 框架(模板'edit_page.html')中创建一个“编辑”页面,用户应该能够编辑存储在数据库中的现有数据。现在我可以轻松地检索用户表单(Django 模板)中的现有原始数据,我的问题是我需要在保存表单之前转换用户填充的数据。

我的模型中有一个“ipv4”字段,用于映射数据库表列,其中 IPv4 地址存储为 unsigned int。例如 ip 地址“10.0.0.0”在数据库中存储为 167772160。 'ipv4' 的数据转换可以实现如下(示例):

# Convert from raw database to human friendly format (167772160 -> 10.0.0.0)
import ipaddress
ipv4_raw = 167772160
ipv4_friendly = str(ipaddress.ip_address(ipv4_raw))
print(ipv4_friendly)
# Output: 10.0.0.0

# Convert from human friendly format to raw database format (10.0.0.0 -> 167772160 )
import ipaddress
ipv4_friendly = '10.0.0.0'
ipv4_raw = int(ipaddress.ip_address(ipv4_friendly))
print(ipv4_raw)
# Output: 167772160

我的表单需要向用户显示人性化的“ipv4”,但必须能够以“原始”格式 (int) 更新数据库列。

'ipv4' 必须从原始格式转换为用户友好格式才能显示,反之则用于数据库更新。

我应该在哪里执行此操作以及如何执行?到目前为止,我有以下实现,其中 IP 以原始格式显示。

models.py

from django.db import models

class Items(models.Model):
    ipv4 = models.PositiveIntegerField()

    class Meta:
        managed = False
        db_table = 'Items'

forms.py

from django import forms
from .models import Items

MyEditForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['ipv4'] = forms.CharField(label='IPv4',widget=forms.TextInput(attrs={ 'class': 'form-control' }))

    def clean_ip(self):
        import ipaddress
        ip = int(ipaddress.ip_address(self.cleaned_data['ipv4']))
        return ipv4

    class Meta(object):
        model = Items
        fields = ('ipv4')

views.py

def item_edit(request,item_id):

    item_id = int(item_id)
    item = Items.objects.get(id = item_id)

    redirect_url = 'https://domain.tld/items/' + str(item_id)

    if request.method == 'POST':  
        my_edit_form = MyEditForm(request.POST,instance=item)

        if my_edit_form.is_valid():
            my_edit_form.save()
            return redirect(redirect_url)
    else:
        my_edit_form = MyEditForm(instance=item)

    return render(request,'device_edit_form.html',{
        'item': item,
        'my_edit_form': my_edit_form
    })

editpage.html

{% block page_content %}
    <form id="my-edit-form" method="post" action="" role="form">
    {% csrf_token %}
        <div class="form-group">
            {{ my_edit_form.ipv4.label_tag }}
            {{ my_edit_form.ipv4 }}
    </div>
</form>
{% endblock %}

【问题讨论】:

    标签: django python-3.x


    【解决方案1】:

    您可以创建自定义字段,例如:

    import ipaddress
    
    class MyIPField(forms.CharField):
        def prepare_value(self, value):
            if value:
                return str(ipaddress.ip_address(value))
            return value
    
        def to_python(self, value):
            try:
                ip = int(ipaddress.ip_address(value))
            except ValueError:
                raise ValidationError('Not valid IP address')
    
            return ip
    

    并以__init__的形式引用它:

    self.fields['ipv4'] = MyIPField(label='IPv4', widget=forms.TextInput(attrs={'class': 'form-control'}))
    

    在这种情况下,您不需要clean_ip 方法。

    另一种解决方案是拥有两个模型字段(用于原始和友好 ip)并在表单验证成功后手动保存原始值。

    【讨论】:

    • 谢谢,当 'ipv4' 字段具有有效 IP 时,这很有效,但是当输入的数据不是预期的类型时会引发错误,这会阻止模板页面的呈现。为什么在这种情况下,表单错误字段机制(例如 ipv4.errors)不处理错误?如何在 UI 中显示错误,例如:

      {{ my_edit_form.ipv4.name }} : {{ ipv4.errors }}

      ?
    猜你喜欢
    • 1970-01-01
    • 2018-07-07
    • 2018-10-10
    • 1970-01-01
    • 2011-02-19
    • 2013-08-19
    • 2012-02-27
    • 2020-10-11
    相关资源
    最近更新 更多