Daily Archives: 11/04/2016

Ruby – Các thành phần của Ruby

Một ngôn ngữ lập trình bao gồm nhiều thành phần cấu thành nên nó. Trong phần này chúng ta sẽ tìm hiểu các thành phần cấu tạo nên ngôn ngữ lập trình Ruby.

Bình luận – Comment

Các đoạn comment được dùng để ghi chú mã nguồn. Cú pháp comment của Ruby có 2 loại là comment cho một dòng và comment cho nhiều dòng. Comment trên một dòng được bắt đầu bởi dấu #, comment trên nhiều dòng được bọc bởi cặp kí hiệu =begin=end.

=begin
  comments.rb
  Pho Code
=end

puts "Comments example"

Các dòng comment sẽ không được dịch bởi trình thông dịch.

=begin
  comments.rb
  Pho Code
=end

Biến – Variable

Biến chỉ là một cái tên, đại diện cho một thứ gì đó có công việc là lưu trữ một giá trị nào đó. Trong lập trình thì chúng ta nói là “gán giá trị cho biến”, giá trị ở đây có thể là một đoạn text, một con số hay một đối tượng nào đó.

Tên của các biến được đặt bằng các kí tự trong bảng chữ cái và dấu gạch dưới, nhưng không được bắt đầu bằng kí tự số, cũng không được bắt đầu bằng kí tự viết HOA, nếu dùng chữ HOA thì Ruby sẽ gọi đây là “hằng số”.

Value
value2
company_name

Value là một hằng số, value2company_name là biến.

12Val
exx$
first-name

Ở trên là các tên biến không hợp lệ.

Tên biến có thể được bắt đầu bởi 2 kí tự đặc biệt là @$chúng ta sẽ tìm hiểu thêm sau.

Biến trong Ruby là có phân biệt HOA-thường, tức là pricepRice là 2 biến khác nhau.

Hằng số – Constant

Trong các ngôn ngữ khác thì hằng số cũng là các biến thôi nhưng chỉ lưu một giá trị trong suốt chương trình, không thể thay đổi được. Nhưng không giống với các ngôn ngữ khác, giá trị của các hằng số trong Ruby là có thể thay đổi được, khi chúng ta thay đổi giá trị hằng thì Ruby không báo lỗi mà chỉ đưa ra mấy dòng cảnh báo.

Tên hằng số được bắt đầu bởi một kí tự viết HOA, thường thì khi đặt tên hằng chúng ta luôn viết hoa toàn bộ các kí tự trong tên.

Name = "Robert"
AGE = 23

Name = "Juliet"

Trong ví dụ trên chúng ta định nghĩa hằng NameAGE, sau đó thay đổi giá trị của Name và Ruby sẽ báo lỗi tương tự như sau.

C:\Project\Ruby>irb constants.rb
constants.rb:4: warning: already initialized constant Name

Giá trị – Literal

Giá trị là các kí tự mô tả một giá trị của một kiểu dữ liệu nào đó, có thể là một con số, một đoạn text… dùng để gán cho các biến. Chúng ta sẽ tìm hiểu về kiểu dữ liệu sau.

age = 29
nationality = "Hungarian"

Trong ví dụ trên thì 29“Hungarian” là giá trị, agenationality là biến.

require 'date'

sng = true
name = "James"
job = nil
weight = 68.5
born = Date.parse("November 12, 1986")

puts "His name is #{name}"

if sng == true
    puts "He is single"
else
    puts "He is in a relationship"
end

puts "His job is #{job}"
puts "He weighs #{weight} kilograms"
puts "He was born in #{born}"

Trong đoạn code trên thì chúng ta có các giá trị có kiểu dữ liệu boolean, kiểu string, kiểu float, kiểu nil – có thể hiểu là kiểu rỗng, kiểu Date.

His name is James
He is single
His job is 
He weighs 68.5 kilograms
He was born in 1986-11-12

Khối lệnh – Block

Khối lệnh là cách để chúng ta gộp nhóm các lênh lại với nhau, bạn sẽ hiểu về khối lệnh nhiều hơn khi thực hành. Khối lệnh trong Ruby được bắt đầu và kết thúc bởi cặp dấu {} hoặc cặp từ khóa do-end.

puts [2, -1, -4, 0].delete_if { |x| x < 0 }
    
[1, 2, 3].each do |e|
    puts e
end

Trong ví dụ trên chúng ta sử dụng cả 2 loại khối lệnh.

Ngoài các câu lệnh tính toán bình thường thì trong lập trình còn có các câu lệnh điều khiển, ví dụ như câu lệnh if, đây là câu lệnh điều kiện, theo sau if là một biểu thức rồi tới một khối lệnh nằm trong cặp từ khóa then-end. Chúng ta sẽ tìm hiểu thêm về các câu lệnh điều kiện sau.

if true then
    puts "Ruby language"
    puts "Ruby script"
end

Sigil

Sigil là các kí tự $@ dùng để khai báo phạm vi hoạt động của biến. Trong đó $ cho biết biến đó là một biến toàn cục, @ cho biết đó là biến instance, @@ là biến class. Chúng ta sẽ tìm hiểu thêm trong bài lập trình hướng đối tượng.

$car_name = "Peugeot"
@sea_name = "Black sea"
@@species = "Cat"

Sigil luôn được đặt trước tên biến.

Toán tử

Toán tử là các kí tự thực hiện các hành động nào đó trên một giá trị nào đó.

!    +    -    ~    *    **    /    %
<< >>    &    |    ^
==    ===    !=    <=>    >=    >
<    <=    =    %=    /=    -=
+=    *=    **=    ..    ...    not
and    or    ?:    &&    || 

Chúng ta sẽ tìm hiểu thêm về toán tử sau.

Từ khóa

Từ khóa là các từ ưu tiên trong Ruby, thường dùng làm các câu lệnh thực hiện hành động nào đó, chẳng hạn như in giá trị ra màn hình, thực hiện các công việc lặp đi lặp lại hay thực hiện tính toán. Khi đặt tên biến chúng ta không được đặt tên trùng với từ khóa.

alias    and      BEGIN      begin    break    case
class    def      defined?   do       else     elsif
END      end      ensure     false    for      if
in       module   next       nil      not      or
redo     rescue   retry      return   self     super
then     true     undef      unless   until    when 
while    yield 

 

Ở trên là danh sách các từ khóa có trong Ruby.

Django – Tạo form từ model

Giả sử bạn xây dựng một ứng dụng có sử dụng cơ sở dữ liệu – tức là bạn phải có định nghĩa các lớp model, thường thì bạn sẽ phải tạo các form cho người dùng nhập dữ liệu và các form này thường cũng có các field gần như là giống hoàn toàn với các field có trong model, ví dụ như trong một ứng dụng Blog bạn có một model tên là BlogComment có các field lưu trữ thông tin các comment do người dùng nhập vào, vậy thì bạn cũng phải tạo form để người dùng nhập comment vào bài viết.

Nhưng việc định nghĩa một lớp Model và một lớp Form có các field gần như giống nhau hoàn toàn là rất vô nghĩa, mà lại còn viết từ code HTML hoặc cùng lắm là kế thừa từ lớp Form của Django đi nữa thì code vẫn rất mệt mỏi, vì thế nên Django có thể tự định nghĩa luôn các lớp form giùm bạn từ các lớp model mà bạn đã định nghĩa trước, tức là trong model có các field nào thì Django cũng sẽ tự tạo lớp form có các field tương ứng như thế, bạn không cần phải định nghĩa lại nữa.

Kiểu dữ liệu

Mỗi lớp Field của Model sẽ có một lớp Field của Form tương ứng. Dưới đây là bảng tên các lớp Field tương ứng của ModelForm.

Lưu ý là các lớp ModelField hỗ trợ 3 kiểu khóa ngoại là 1-n, n-n1-1 nhưng bên FormField chỉ có 2 kiểu duy nhất được hỗ trợ là ModelChoiceField tương ứng với ForeignKeyModelMultipleChoiceField tương ứng với ManyToManyField. Cả 2 lớp trên đều nhận một tham số khi khởi tạo là một đối tượng QuerySet.

Mỗi lớp Form Field có chung một số thuộc tính như sau:

  • required: nếu thuộc tính blank của ModelTrue thì required = False và ngược lại.
  • label: thuộc tính này được gán tự động bằng thuộc tính verbose_name của Model và kí tự đầu được viết hoa.
  • help_text: thuộc tính này được gán tự động bằng thuộc tính help_text của model.

Ví dụ

Chúng ta định nghĩa các lớp như sau.

from django.db import models
from django.forms import ModelForm

TITLE_CHOICES = (
    ('MR', 'Mr.'),
    ('MRS', 'Mrs.'),
    ('MS', 'Ms.'),
)

class Author(models.Model):
    name = models.CharField(max_length=100)
    title = models.CharField(max_length=3, choices=TITLE_CHOICES)
    birth_date = models.DateField(blank=True, null=True)
   
class Book(models.Model):
    name = models.CharField(max_length=100)
    authors = models.ManyToManyField(Author)

class AuthorForm(ModelForm):
    class Meta:
        model = Author
        fields = ['name', 'title', 'birth_date']

class BookForm(ModelForm):
    class Meta:
        model = Book
        fields = ['name', 'authors']

Chúng ta định nghĩa 2 lớp ModelAuthorBook, sau đó định nghĩa 2 lớp Form có các Field được tạo tự động từ 2 lớp model là AuthorFormBookForm. Các lớp này được kế thừa từ lớp django.forms.ModelForm thay thì lớp django.forms.Form như trước.

class Meta:
    model = Author
    fields = ['name', 'title', 'birth_date']

Chúng ta định nghĩa model được dùng trong lớp nội Meta, thuộc tính model sẽ tạo các field tương ứng từ lớp Model tương ứng, thuộc tính fields sẽ chọn các trường nào được dùng, nếu chúng ta không khai báo thuộc tính fields thì mặc định Django sẽ dùng tất cả các thuộc tính có trong lớp Model, ngoại trừ thuộc tính id nếu bạn không khai báo thuộc tính khóa chính. Ngoài ra lớp Meta còn có thuộc tính exclude, thuộc tính này trái ngược với thuộc tính fields, tức là thuộc tính này sẽ quy định các trường nào không được phép sử dụng.

Định nghĩa từ lớp ModelForm tương đương với định nghĩa từ lớp Form trong các bài trước như sau:

from django import forms

class AuthorForm(forms.Form):
    name = forms.CharField(max_length=100)
    title = forms.CharField(max_length=3,
    widget=forms.Select(choices=TITLE_CHOICES))
    birth_date = forms.DateField(required=False)

class BookForm(forms.Form):
    name = forms.CharField(max_length=100)
    authors = forms.ModelMultipleChoiceField(queryset=Author.objects.all())

Lưu dữ liệu

Lớp ModelForm có một phương thức tên là save(), phương thức này tạo mới hoặc lưu một đối tượng Model vào cơ sở dữ liệu giống như phương thức save() bên các lớp Model vậy.

Khi khởi tạo các đối tượng ModelForm thì chúng ta có thể truyền các đối tượng model có sẵn vào hàm khởi tạo, nếu chúng ta chỉ truyền vào không thôi thì Django sẽ tạo mới một đối tượng trên CSDL, nếu chúng ta truyền vào và ghi rõ truyền với tham số là instance thì Django sẽ cập nhật dữ liệu trong đối tượng đó. Ví dụ:

>>> from myapp.models import Article
>>> from myapp.forms import ArticleForm
>>> f = ArticleForm(request.POST)
>>> new_article = f.save()

Đoạn code trên tạo đối tượng mới từ đối tượng POST được gửi lên và lưu vào CSDL.

>>> a = Article.objects.get(pk=1)
>>> f = ArticleForm(request.POST, instance=a)
>>> f.save()

Đoạn code trên tạo một đối tượng ArticleForm, truyền dữ liệu từ POST vào rồi truyền cập nhật trên đối tượng a chứ không tạo mới.

Lưu ý là nếu chúng ta không kiểm tra sự đúng đắn của dữ liệu thì khi gọi phương thức save(), Django cũng sẽ tự động làm việc đó và sẽ giải phóng lỗi exception ValueError nếu dữ liệu có lỗi, trong bài trước chúng ta đã dùng một phương thức để kiểm tra dữ liệu là is_valid(), chúng ta sẽ tìm hiểu thêm về cách kiểm tra dữ liệu trong các bài sau.

Tùy chỉnh lớp Field

Như trong bảng tên các lớp ModelFieldFormField tương ứng đã chỉ rõ ở trên, nếu bạn khai báo loại field nào trong lớp model thì Django sẽ tạo lớp field tương ứng bên form. Chẳng hạn như khi bạn khai báo trường DateTimeField bên model thì Django sẽ tạo một trường DateTimeField tương ứng bên form.

Mặc dù tạo form bằng cách code từ đầu với HTML hoặc dùng lớp django.forms.Form sẽ cho bạn quyền điều khiển nhiều hơn nhưng không có nghĩa là ModelForm không cho phép bạn tùy chỉnh các lớp field có sẵn.

Chẳng hạn như để tùy chỉnh kiểu hiển thị thì chúng ta dùng thuộc tính widgets có trong lớp nội Meta. Ví dụ:

from django.forms import ModelForm, Textarea
from myapp.models import Author

class AuthorForm(ModelForm):
    class Meta:
        model = Author
        fields = ('name', 'title', 'birth_date')
        widgets = {
            'name': Textarea(attrs={'cols': 80, 'rows': 20}),
        }

Mặc định lớp CharField sẽ hiển thị thẻ <input type="text">, nhưng đoạn code trên sẽ chỉ định CharField hiển thị <input type="textarea">.

Tương tự bạn có thể tùy chỉnh lại các thuộc tính như label, help_text trong lớp nội Meta nếu muốn:

from django.utils.translation import ugettext_lazy as _

class AuthorForm(ModelForm):
    class Meta:
        model = Author
        fields = ('name', 'title', 'birth_date')
        labels = {
            'name': _('Writer'),
        }
        help_texts = {
            'name': _('Some useful help text.'),
        }

Bạn cũng có thể quy định các thuộc tính phải dùng lớp field do bạn định nghĩa thông qua thuộc tính field_classes trong lớp Meta:

from django.forms import ModelForm
from myapp.models import Article

class ArticleForm(ModelForm):
    class Meta:
        model = Article
        fields = ['pub_date', 'headline', 'content', 'reporter', 'slug']
        field_classes = {
            'pub_date': MyDateTimeField,
        }

class MyDateTimeField():
    #...
    pass

Thừa kế Form

ModelForm cũng chỉ là một lớp Python, do đó bạn có thể cho thừa kế và mở rộng các trường hoặc các phương thức của chúng. Ví dụ:

class EnhancedArticleForm(ArticleForm):
    def clean_pub_date(self):
        #... 
        pass

Lớp EnhancedArticleForm kế thừa từ lớp ArticleForm nên sẽ có tất cả các thuộc tính và phương thức của lớp ArticleForm, ngoài ra lớp EnhancedArticleForm còn có phương thức clean_pub_date() của riêng nó nữa.

Tương tự với các lớp Model, các lớp form cũng có thể thừa kế cả lớp nội Meta:

class RestrictedArticleForm(EnhancedArticleForm):
    class Meta(ArticleForm.Meta):
        exclude = ('body',)

Lớp RestrictedArticleForm kế thừa từ lớp EnhancedArticleForm ngoại trừ lớp này không sử dụng thuộc tính body.

Lưu ý là khi sử dụng đa thừa kế thì lớp Meta của lớp con chỉ thừa kế từ lớp Meta của lớp cha đầu tiên trong danh sách thôi.

Hàm factory

Nếu bạn quá “lười” để ngồi định nghĩa lại một lớp kế thừa từ ModelForm, sau đó ngồi khai báo các thuộc tính fields, exclude hay lớp Meta thì Django cũng cung cấp một hàm cho phép bạn tạo một lớp ModelForm một cách “cấp tốc” là hàm modelform_factory(), ví dụ:

from django.forms import modelform_factory
from myapp.models import Book

BookForm = modelform_factory(Book, fields=("author", "title"))

Hàm này đặc biệt hữu dụng khi lớp Form mà bạn muốn định nghĩa không có gì khác nhiều so với lớp cha.

Bạn cũng có thể đưa các thuộc tính chỉnh sửa vào ngay trong hàm modelform_factory():

from django.forms import Textarea
Form = modelform_factory(Book, form=BookForm, widgets={"title": Textarea()}, fields=['name'])