2012-01-10 45 views
14

Tôi đã tạo một biểu mẫu Django gửi đến một trang trên một tên miền khác (mà tôi không kiểm soát). Ý tưởng là tôi có một mẫu được tạo kiểu gọn gàng, đẹp mắt phù hợp gọn gàng vào trang web của riêng tôi và đưa người dùng đến nơi khác khi được gửi.Ghi đè tên của trường biểu mẫu Django

Tuy nhiên,

  • Nếu mà hình thức khác thay đổi tên của bất kỳ lĩnh vực của mình, tôi cần phải thay đổi tên của các trường trong hình thức của tôi và sau đó thay đổi những cái tên đó ở khắp mọi nơi khác trong ứng dụng của tôi - kể từ khi name attr được kết hợp với tên của thuộc tính được sử dụng cho trường.
  • Nếu biểu mẫu từ xa sử dụng tên ngớ ngẩn thì đối tượng biểu mẫu của tôi cũng phải có các thuộc tính có tên ngớ ngẩn gây ô nhiễm mã của ứng dụng của tôi.
  • Nếu những tên đó xảy ra là reserved words in Python (ví dụ: from), thì sẽ khó hoặc không thể tạo biểu diễn đối tượng biểu mẫu Django.

Có cách nào để chỉ định một chuỗi khác nhau để sử dụng cho thuộc tính 'tên' khi trường được hiển thị không? Vì vậy, hãy tách biểu mẫu HTML khỏi lớp đại diện cho nó.

này sẽ cần hai thành phần:

  1. Ghi đè giá trị tên trong widget
  2. Tận dụng hình thức đọc giá trị này từ request.POST khi nó được ràng buộc

Tôi chỉ cần bước 1 trong trường hợp này, nhưng bước 2 có liên quan để giải quyết các vấn đề tôi đã liệt kê ở trên thông thường hơn

Trả lời

24

Đây là một khá khủng khiếp một chiếc xe buýt e của API, nhưng có một phương thức biểu mẫu được gọi là add_prefix được gọi để xác định tên HTML của mỗi trường nên, có tính đến tiền tố của biểu mẫu nếu có. Bạn có thể ghi đè lên đó để nó nhìn lên tên trường trong một cuốn từ điển ở đâu đó và trả về tên mà bạn muốn - không quên để bảo vệ hành vi tiền tố hiện có:

FIELD_NAME_MAPPING = { 
    'field1': 'html_field1', 
    'field2': 'html_field2' 
} 

class MyForm(forms.ModelForm): 
    def add_prefix(self, field_name): 
     # look up field name; return original if not found 
     field_name = FIELD_NAME_MAPPING.get(field_name, field_name) 
     return super(MyForm, self).add_prefix(field_name) 
+0

Đây là một giải pháp tuyệt vời cho vấn đề của tôi và vấn đề chung của attrs tên trường được gắn với tên thuộc tính của đối tượng trường. Tuy nhiên, nếu bạn cần điều này thì bạn có thể có một vấn đề. Trước tiên hãy kiểm tra xem câu trả lời của S.Lott có phù hợp với bạn hay không. Nó không có tác dụng với tôi, đó là lý do tại sao tôi hỏi câu hỏi này. – adamnfish

+0

@Daniel: Gần đây tôi đã thực hiện điều chính xác như bạn đã đăng trên câu trả lời của mình. Bạn có thể làm rõ lý do tại sao bạn cho rằng đây là lạm dụng API khủng khiếp? cảm ơn. –

+0

Thành thật mà nói, điều này đã gần bốn năm trước và tôi không thể nhớ tại sao tôi lại nghĩ vậy. –

3

tên tại tr được kết hợp với tên của thuộc tính được sử dụng cho trường.

Từ mô tả của bạn ("Nếu biểu mẫu khác thay đổi tên của bất kỳ trường nào, tôi cần thay đổi tên của trường trong biểu mẫu của tôi và sau đó thay đổi tên đó ở mọi nơi khác trong ứng dụng của tôi -" "Nếu biểu mẫu từ xa sử dụng tên ngớ ngẩn thì đối tượng biểu mẫu của tôi cũng phải có các thuộc tính có tên ngớ ngẩn gây ô nhiễm mã ứng dụng của tôi.") Bạn nhận ra đây là một quyết định thiết kế mong manh.

Bạn nên xem xét rằng chức năng chế độ xem của bạn hoàn toàn giải quyết được vấn đề này một cách tầm thường.

Thay vì căn chỉnh tên giữa ứng dụng từ xa và ứng dụng của bạn, hãy sử dụng chức năng xem của bạn để ánh xạ từ những cái tên đẹp đến tên khủng khiếp của chúng.

Đây là chức năng của chế độ xem.

Để tiến thêm một bước nữa, chức năng xem của bạn thực hiện ba điều.

  1. Xác thực đầu vào. Có lẽ tồn tại chúng trong một số cơ sở dữ liệu địa phương.
  2. Dữ liệu bản đồ từ biểu mẫu của bạn đến cấu trúc yêu cầu của chúng.
  3. Thực hiện yêu cầu từ xa (qua httplib hoặc urllib2 hoặc bất kỳ thứ gì).

Mục 1 và 3 không thay đổi nhiều.

Mục 2 là ánh xạ trường theo trường từ yêu cầu.POST đến từ điển mà sau đó bạn url lib.urlencode để tạo yêu cầu POST. (Hoặc bất kỳ giao thức nào.)

Vì vậy, hãy chia nhỏ mục 2 thành một điều linh hoạt mà bạn chỉ định trong cài đặt của mình.

thiết lập

MY_MAPPING_FUNCTION = module.function 

Trong views.py bạn

def submit(request): 
    if request.method == POST: 
     form = SomeForm(request.POST) 
     if is_valid(form): 
      form.save() 
      to_be_submitted = settings.MY_MAPPING_FUNCTION(form) 
      remote_post(to_be_submitted) # or whatever your protocol is 

Thêm module lập bản đồ để ứng dụng của bạn

module.py

def version_1_2(form): 
    return { 
     'silly_name_1': form.cleaned_data['your_nice_name'], 
     'from': form.cleaned_data['another_nice_name'], 
    } 

def version_2_1(form): 
    return { 
     'much_worse_name': form.cleaned_data['your_nice_name'], 
     'from': form.cleaned_data['another_nice_name'], 
    } 
+0

Cảm ơn, nhưng tôi nhận thấy điều này là tầm thường nếu tôi đăng bài lên chế độ xem tôi kiểm soát. Vấn đề là, người dùng phải được đưa đến một trang web khác khi biểu mẫu gửi. Bạn đã nói 'remote_post'. Đó là tốt để làm trên máy chủ nhưng tôi không thể phát hành một "chuyển hướng bài viết" cho khách hàng vì vậy trừ khi tôi muốn tái thực hiện logic kinh doanh của họ (tôi không) Tôi buộc phải đặt hành động của biểu mẫu cho trang web của họ. – adamnfish

+1

"Tôi không thể phát hành" chuyển hướng bài đăng "cho khách hàng". Nó không cần thiết.Bạn có thể gửi biểu mẫu qua 'urllib2', nhận phản hồi và sau đó tải xuống trang phản hồi của trang web khác từ trình duyệt của bạn một cách trivially. Chuyển hướng đã xuất hiện để xảy ra. Phần khó nhất về việc này là đảm bảo rằng cookie của trang web từ xa được bao gồm trong quá trình chuyển giao cho trình duyệt của người dùng. Bạn đang xây dựng một cuộc tấn công "man-in-the-middle". –

+0

Tôi đã xem xét điều này và tiếc là tôi không thể. Phản hồi chứa các URL nội dung tương đối vì vậy tôi phải viết một trình phân tích cú pháp để chuyển đổi tất cả các đường dẫn trong phản hồi thành các URL tuyệt đối trên tên miền của chúng. "và sau đó tải xuống trivially trang phản hồi của trang web khác" là không có điều như vậy! – adamnfish

6

Tôi đã thực hiện một chức năng đơn giản mà ghi đè widget render phương pháp và gán một tên tùy chỉnh:

def namedWidget(input_name, widget=forms.CharField): 
    if isinstance(widget, type): 
     widget = widget() 

    render = widget.render 

    widget.render = lambda name, value, attrs=None: \ 
     render(input_name, value, attrs) 

    return widget 

Việc sử dụng rất đơn giản:

class AliasCreationForm(forms.Form): 
    merchant_id = forms.CharField(
     max_length=30, 
     widget=namedWidget('PSPID', forms.HiddenInput), 
    ) 
+0

Một sửa đổi nhỏ cho giải pháp, phụ tùng phải được thông qua như arg đầu tiên cho cuộc gọi hàm lambda. widget.render = lambda name, value, attrs = None: \ render (widget, input_name, value, attrs) – kumar

1

tôi nhận ra câu hỏi này là cũ, nhưng đây là một cách khác (có thể sạch hơn) để làm điều đó. Hầu hết các câu trả lời ở đây liên quan đến việc vá khỉ hoặc ghi đè phương thức siêu lớp. Điều này là tốt, nhưng tôi nghĩ rằng đó là một cách sạch sẽ để làm điều đó.

Tạo Widget mới mà kéo dài widget bạn cần, chẳng hạn như TextInput:

class TextInputSansName(forms.TextInput): 
    def build_attrs(self, extra_attrs=None, **kwargs): 
     if extra_attrs: 
      extra_attrs.pop('name', None) 
     kwargs.pop('name', None) 
     return super().build_attrs(extra_attrs, **kwargs) 

Django gọi build_attrs trên một widget trong khi phẫu thuật render(). Trong phương pháp này, tôi sẽ xóa 'tên' nếu nó nằm trong một trong các từ điển. Điều này có hiệu quả loại bỏ tên ngay trước khi nó ám.

Câu trả lời này ghi đè ít nhất có thể của API Django.

Trong một trong các trang web của tôi, bộ xử lý thanh toán cần đầu vào không có tên. Dưới đây là tuyên bố kiểm soát trong lớp biểu mẫu:

card_number = forms.IntegerField(
    label='Card Number', 
    widget=TextInputSansName(attrs={ 'data-stripe': 'number' }) 
) 

Chúc mừng.

+0

Điều này rất hay, nhưng câu hỏi là cách sử dụng * tên * khác nhau (được chỉ định) cho các trường, chứ không phải như thế nào để loại bỏ chúng hoàn toàn. Cảm ơn câu trả lời! – adamnfish

Các vấn đề liên quan