Djangular

یکی از دغدغه های من آوردن AngularJS به Django بوده و این موضوع کاملا با Djangular پوشش داده میشه :)))

مراحل کار :

pip install django-angular
INSTALLED_APPS = (
    ...
    'djangular',
    ...
)

STATIC_ROOT and STATIC_URL فراموش نشود و سپس جرای دستور :

python manage.py collectstatic

دستور بالا مسیر استاتیک را با فایل های مورد نیاز دیجنگولار بروز رسانی میکند یعنی باید مسیر مورد نظر مجوز ویرایش را داشته باشد هاا

البته دیجانگولار هیچ مدل داده ای ندارد و بدون تغییرات بانک اطلاعاتی می تواند عملیات خود را ادامه دهد

 Integrate AngularJS with Django

XMLHttpRequest

معمولا در وب سایت ها درخواست های آژادکسی هدر خود را بصورت زیر ارسال میکنند :

X-Requested-With: XMLHttpRequest

این هدر است که به دیجانگو امکان میدهد تا بفهمد :

request.is_ajax()

حالا برای اینکه این امکان رو فراهم کنیم کافیگ زیر را برای appمون منویسیم

var my_app = angular.module('MyApp').config(function($httpProvider) {
    $httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
});

 Template tags

برای اینکه تگ های آنگولار با تگ های دیجاگو گیژ نزنن کد زیر رو میزنیم :

var my_app = angular.module('MyApp').config(function($interpolateProvider) {
    $interpolateProvider.startSymbol('{$');
    $interpolateProvider.endSymbol('$}');
});

راستی این رو هم اضافه کنید :

<script src="{% static 'djangular/js/django-angular.min.js' %}" type="text/javascript"></script>

و سپس :

var my_app = angular.module('myApp', [/* other dependencies */, 'ng.django.forms']);

حالا تو لینک زیر یه مثال خوب هست که بهش توجه کنیم :

https://github.com/jrief/django-angular/tree/master/examples

Integrate a Django form with an AngularJS model

هر گاه می خواهیم forms.Form رو ببریم بسمت AngularJS Env برای ما لازم است که به یک صفحه ی رندر شده ی AngularJSی دسترسی پیدا کنیم

ng-model="model_name"

where model_name corresponds to the named field from the declared form class. :)))

کد نمونه :

در نظر بگیریم که یک کلاس فرم ساده دیجانگو داریم که از یک فرم ورود متن ساده تشکیل شده است و ما می خواهیم دهنش رو با آوردن به djangularjs کلاس NgModelFormMixin سرویس کنیم

from django import forms
from django.utils import six
from djangular.forms import NgDeclarativeFieldsMetaclass, NgModelFormMixin

class ContactForm(six.with_metaclass(NgDeclarativeFieldsMetaclass, NgModelFormMixin, forms.Form)):
    subject = forms.CharField()
    # more fields ...

در مثال بالا همونطور که معلومه فرم ما از دیجانگو فرم برگفته شده است و در عوض میشه اینطوری هم نوشتش :

from djangular.forms import NgModelFormMixin, NgForm

class MyValidatedForm(NgModelFormMixin, NgForm):
    # members as above

و همینطور اینگونه :

from djangular.forms import NgModelFormMixin, NgModelForm

class MyValidatedForm(NgModelFormMixin, NgModelForm):
    class Meta:
         model = Article

    # fields as usual

حالا آبجکت های اینطوری رندر میشن :

<input id="id_subject" type="text" name="subject" ng-model="subject" />

حالا یه مثال کامل :

مثال زیر نشان دهنده ی ارسال داده هاست بوسیله ی انگولارجی اس کنترولر. و ویروی دیجانگو که قراره کنترل کننده ی اون باشه به شکل زیره :

from django.views.generic import TemplateView

class ContactFormView(TemplateView):
    template = 'contact.html'

    def get_context_data(self, **kwargs):
        context = super(ContactFormView, self).get_context_data(**kwargs)
        context.update(contact_form=ContactForm())
        return context

و حالا تمپلیت contact.html

<form ng-controller="MyFormCtrl" name="contact_form">
    {{contact_form}}
    <button ng-click="submit()">Submit</button>
</form>

یه کمی هم جاوا اسکریپت قاطیش می کنیم :

my_app.controller('MyFormCtrl', function($scope, $http) {
    $scope.submit = function() {
        var in_data = { subject: $scope.subject };
        $http.post('/url/of/your/contact_form_view', in_data)
            .success(function(out_data) {
                // do something
            });
    }
});

و اما اینکه <form> نیازی به method or action نداره و از طریق success کنترلر ارسال میشه و تمام ابعاد قضیه رو شامل ارسال و پاسخ رو در نظر میگیره مثل ارسال ارورهای اعتبارسنجی شده و …

به طور معمول form view میبایست داده های پست شده رو از طریق POST دریافت کنه در حالی که AngularJS دیتا رو از طریق multipart/form-data or application/x-www-form-urlencoded نمی تونه ارسال کنه البته به خاطر یه سری مسایل دیکودینگ هااا

برای جواب داده به فرم هم بهتره از کد زیر استفاده بشه :

import json
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponseBadRequest

class ContactFormView(TemplateView):
    # use ‘get_context_data()’ from above

    @csrf_exempt
    def dispatch(self, *args, **kwargs):
        return super(ContactFormView, self).dispatch(*args, **kwargs)

    def post(self, request, *args, **kwargs):
        if not request.is_ajax():
            return HttpResponseBadRequest('Expected an XMLHttpRequest')
        in_data = json.loads(request.body)
        bound_contact_form = CheckoutForm(data={'subject': in_data.get('subject')})
        # now validate ‘bound_contact_form’ and use it as in normal Django

در مثال بالا csrf_exempt رو زدیم ها ولی بکار نبریدش کلا یعنی همیشه از امنیتش استفاده کنید

Prefixing the form fields

The problem with this implementation, is that one must remember to access each form field three times. Once in the declaration of the form, once in the Ajax handler of the AngularJS controller, and once in the post handler of the view. This make maintenance hard and is a violation of the DRY principle. Therefore it makes sense to add a prefix to the model names. One possibility would be to add the argument scope_prefix on each form’s instantiation, ie.:

متن بالا رو نمی تونم ترجمه کنم خدتون بخونید بهتره

حالا برای اینکه مشکل متن بالا پیش نیاد بهتره از prefixاستفاده کنیم

class ContactForm(NgModelFormMixin, forms.Form):
    # declare form fields

    def __init__(self, *args, **kwargs):
        kwargs.update(scope_prefix='my_prefix')
        super(ContactForm, self).__init__(*args, **kwargs)

حالا خروجی تو آنگولار به شکل زیر میشه میشه :

<input id="id_subject" type="text" name="subject" ng-model="my_prefix.subject" />

این بالاییه حالا میتونه ارسال آزاکسی رو راحتتر کنه برای اینکه هر ارسال بروی یک آبجکت جاوا اسکریپتی انجام میشه :)) و میشه اون رو بصورت  scope.my_prefix ارسال نمود

$http.post('/url/of/contact_form_view', $scope.my_prefix)

Validate Django forms using AngularJS

NgFormValidationMixin

from django import forms
from django.utils import six
from djangular.forms import NgDeclarativeFieldsMetaclass, NgFormValidationMixin

class MyValidatedForm(six.with_metaclass(NgDeclarativeFieldsMetaclass, NgFormValidationMixin, forms.Form)):
    form_name = 'my_valid_form'
    surname = forms.CharField(label='Surname', min_length=3, max_length=20)
    age = forms.DecimalField(min_value=18, max_value=99)

باز نویسی کنیم به روش ساده تری :

from djangular.forms import NgFormValidationMixin, NgForm

class MyValidatedForm(NgFormValidationMixin, NgForm):
    # members as above
from djangular.forms import NgFormValidationMixin, NgModelForm

class MyValidatedForm(NgFormValidationMixin, NgModelForm):
    class Meta:
         model = Article

    # fields as usual

هر صفحه ای انگولارجی اس نیاز به یک نام منحصربفرد داره در غیر اینصورت اعتبار سنجی ها شاسکول میشن. در نتیجه باید هر فرم از NgFormValidationMixin گرفته بشه

اگر در یک صفحه فقط یک فرم داشته باشیم، نام فرم میتواند در به class declaration مثل مثال پایین اضافه بشه :

تک فرمی :

def get_context_data(self, **kwargs):
    context = super(MyRenderingView, self).get_context_data(**kwargs)
    context.update(form=MyValidatedForm())
    return context

چند فرمی :

def get_context_data(self, **kwargs):
    context = super(MyRenderingView, self).get_context_data(**kwargs)
    context.update(form1=MyValidatedForm(form_name='my_valid_form1'),
                   form2=MyValidatedForm(form_name='my_valid_form2'))
    return context

 Render this form in a template

<form name="{{ form.form_name }}" novalidate>
  {{ form }}
  <input type="submit" value="Submit" />
</form>

Use the directive novalidate to disable the browser’s native form validation

این هم برای دکمه ی سابمیت :

<input type="submit" class="btn" ng-disabled="{{ form.form_name }}.$invalid" value="Submit">

 More granular output

from django import forms
from djangular.forms import NgFormValidationMixin

class MyValidatedForm(NgFormValidationMixin, forms.Form):
    email = forms.EmailField(label='Email')
<ul class="djng-form-errors" ng-hide="subscribe_form.email.$pristine">
  <li ng-show="subscribe_form.email.$error.required" class="ng-hide">This field is required.</li>
  <li ng-show="subscribe_form.email.$error.email" class="">Enter a valid email address.</li>
</ul>

 Combine NgFormValidationMixin with NgModelFormMixin

from django import forms
from djangular.forms import NgFormValidationMixin, NgModelFormMixin

class MyValidatedForm(NgModelFormMixin, NgFormValidationMixin, forms.Form):
    # custom form fields

but don’t do this

class MyValidatedForm(NgFormValidationMixin, NgModelFormMixin, forms.Form):
    # custom form fields

and this :

form = MyValidatedForm(form_name='my_form', scope_prefix='my_model')

css class for errors :

ul.djng-form-errors {
        margin-left: 0;
        display: inline-block;
        list-style-type: none;
}
ul.djng-form-errors li.invalid {
        color: #e9322d;
}
ul.djng-form-errors li.invalid:before {
        content: "\2716\20";  /* adds a red cross before the error message */
}
ul.djng-form-errors li.valid:before {
        color: #00c900;
        content: "\2714";  /* adds a green tick */
}

If you desire an alternative CSS class or an alternative way of rendering the list of errors, then initialize the form instance with

class MyErrorList(list):
    # rendering methods go here

# during form instantiation
my_form = MyForm(error_class=MyErrorList)

توجه کنید و مثال های دیگر

from django import forms

class MyForm(forms.Form):
    # other fields
    date = forms.DateField(label='Date',
        widget=forms.widgets.DateInput(attrs={'validate-date': '^(\d{4})-(\d{1,2})-(\d{1,2})$'}))
<input name="date" ng-model="my_form_data.birth_date" type="text" validate-date="^(\d{4})-(\d{1,2})-(\d{1,2})$" />
angular.module('MyApp', ['ng.django.forms']);

دیدگاهتان را بنویسید