Kết nối django với mysql

Sau khi chia sẻ một chút về CRUD với nodejs thì hôm nay, mình sẽ chia sẻ thêm một chút CRUD về python với Django sử dụng Mysql. Và tất nhiên nội dung cơ bản mình chia sẻ chỉ nằm trong phạm vi dành cho những người bắt đầu học python mà thôi.

Trong bài viết này mình sử dụng Python 2.7.12, Django 1.11 có sử dụng Function views và Base views.

Vì mình chỉ chia sẻ một số chức năng cơ bản nên có thể sẽ thiếu sót trong quá trình chạy project ví dụ như chưa có Authentication, validate cho form,... nên các bạn có thể tự tìm hiểu thêm

Kết nối django với mysql
)

1. Khởi tạo project Django

Điều đầu tiên cần làm là khởi tạo 1 project, trong ví dụ này mình sẽ tạo 1 project tên là myproject:

~$ django-admin startproject myproject

Xong, chúng ta có cây thư mục như sau:

myproject
--myproject
----__init__.py
----settings.py
----urls.py
----wsgi.py
--manage.py

2. Cấu hình database MySql

Mở file settings.py và tìm đến chỗ cáu hình db DATABASES:

# settings.py
...
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql', 
        'NAME': <db_name>,
        'USER': <db_user>,
        'PASSWORD': <db_password>,
        'HOST': 'localhost',
        'PORT': '',
    }
}
...

Config xong, bạn cần migrate db bằng cách:

~$ python manage.py migrate

Và tạo một super user cho hệ thống của bạn:

~$ python manage.py createsuperuser

Sau khi tạo username, email, password cho super user thì công việc tiếp theo là khởi chạy hệ thống:

~$ python manage.py runserver

Xong, khởi đầu thành công với http://localhost:8000

3. Tạo module (app) mới.

Tiếp theo cần tạo một module hay cũng có thể gọi là 1 app mới cho hệ thống để chúng ta có thể dễ dàng hơn cho việc triển khai chức năng CRUD. Ở đây sẽ tạo module cho đối tượng Post.

~$ python manage.py startapp posts

Chúng ta sẽ có cây thư mục như sau:

Kết nối django với mysql
Tiếp theo cần đăng ký cho module mới tạo vào trong hệ thống:

#settings.py

INSTALLED_APPS = [
    'posts',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

3.1. Cấu hình models

# posts/models.py

from __future__ import unicode_literals

from django.db import models
from django.core.urlresolvers import reverse

class Post(models.Model):
  name = models.CharField(max_length=224, null=False, blank=False)
  content = models.TextField(max_length=254, null=False, blank=False)
  timestamp = models.DateTimeField(auto_now_add=True)
  updated = models.DateTimeField(auto_now=True)

  def __unicode__(self):
    return self.content

Sau lần lượt chạy:

~$ python manage.py makemigrations

Sẽ thấy:

Migrations for 'posts':
  posts/migrations/0001_initial.py
    - Create model Post

Tieps theo là migrate:

~$ python manage.py migrate

4. CRUD

4.1 List Post

# posts/views.py

from __future__ import unicode_literals

from django.shortcuts import render, redirect
from django.views.generic import ListView

from .models import Post

class ListPostView(ListView):
  model = Post
  def get (self, request, *args, **kwargs):
    template_name = 'posts/list-posts.html' # sẽ được tạo ở phần dưới
    obj = {
      'posts': Post.objects.all()
    }
    return render(request, template_name, obj)

Trong urls.py

# posts/urls.py

from django.conf.urls import url
from django.contrib import admin
from posts.views import (
  ListPostView,
)

urlpatterns = [
    url(r'^list-posts/$', ListPostView.as_view(), name='list-posts'),
]

Để sử dụng namespace trong python, thì cần thêm vào myproject/urls.py

# myproject/urls.py

from django.conf.urls import url, include
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^posts/', include('posts.urls', namespace='posts')),
]

Tiếp theo là template, ở đây mình sẽ hướng dẫn các bạn sử dụng layout luôn:

<!-- posts/templates/layouts/layout.html -->

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>{% block title %}CRUD{% endblock title %}</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/css/bootstrap.min.css">
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.0/jquery.min.js"></script>
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/js/bootstrap.min.js"></script>
        <style>
          footer {
            background-color: #555;
            color: white;
            padding: 15px;
          }
        </style>
    </head>
    <body>
        <div class="container-fluid">
            <div class="row content">
              <div class="col-sm-3 sidenav">
                <h4>CRUD</h4>
                <ul class="nav nav-pills nav-stacked">
                  <li {% if request.path == "/posts/list-posts/" %}class="active"{% endif %}>
                    <a href="{% url 'posts:list-posts' %}">List Posts</a>
                  </li>
                </ul><br>
              </div>        
              <div class="col-sm-9">
                  {% block content %}
                  {% endblock content %}
              </div>
            </div>
          </div>          
          <footer class="container-fluid">
            <p>My Project CRUD Posts</p>
          </footer>
    </body>
</html>

Và đến posts/list-posts.html

<!-- posts/templates/posts/list-posts.html -->

{% extends "layouts/layout.html" %}
{% block title %}
    List Post | {{ block.super }}
{% endblock title %}
{% block content %}
  <h4><small>List Posts</small></h4>
  <hr>
  {% if posts %}
    {% for post in posts %}
    <h2>{{post.name}}</h2>
    <h5><span class="glyphicon glyphicon-time"></span> Post at {{post.timestamp}}</h5>
    <p>{{post.content}}</p>
    <br><br>
    {% endfor %}
  {% else %}
    No song.
  {% endif %}
{% endblock content %}

Như vậy là chúng ta đã có thể xem list các bài post rồi. Nhưng hiện tại vẫn chưa có dữ liệu. Nào, bắt đầu thêm dữ liệu nhé.

4.2 Create Post

Để tạo được bài post, đầu tiên chúng ta nên tạo 1 file gọi là forms.py:

# posts/forms.py

from django import forms
from .models import Post

class CreatePostForm(forms.ModelForm):
  class Meta:
    model = Post
    fields = ['name', 'content',]

Và urls thêm:

# posts/urls.py
...
from posts.views import (
  ListPostView,
  CreatePostView,
)
urlpatterns = [
    url(r'^list-posts/$', ListPostView.as_view(), name='list-posts'),
    url(r'^create-post/$', CreatePostView.as_view(), name='create-post'),
]

Quay lại với file views

# posts/views.py


...
from django.views.generic import ListView, CreateView
from django.contrib.messages.views import SuccessMessageMixin
from .forms import CreatePostForm
...
...
class CreatePostView(SuccessMessageMixin, CreateView):
  template_name = 'posts/create-post.html'
  form_class = CreatePostForm
  success_message = 'Crate Post successfully!'

Và templates:

<!-- posts/templates/layouts/layout.html -->
...
<ul class="nav nav-pills nav-stacked">
  {% if messages %}
    {% for message in messages %}
      <div class="alert alert-success">
        <strong>{{ message }}</strong>
      </div>
    {% endfor %}
  {% endif %}
  <li {% if request.path == "/posts/list-posts/" %}class="active"{% endif %}>
    <a href="{% url 'posts:list-posts' %}">List Posts</a>
  </li>
  <li {% if request.path == "/posts/create-post/" %}class="active"{% endif %}>
    <a href="{% url 'posts:create-post' %}">Create Posts</a>
  </li>
</ul><br>
...
<!-- posts/templates/posts/create-post.html -->

{% extends "layouts/layout.html" %}
{% block title %}
    Create Post | {{ block.super }}
{% endblock title %}
{% block content %}
  <h4><small>Create Posts</small></h4>
  <hr>
  <div class="card-body">
    <div class="table-responsive">
      <form action="{% url 'posts:create-post' %}" method="POST">
        {% csrf_token %}
        <div class="form-group">                    
          <label for="comment">Name<span style="color: red">*</span> :</label>
          <input type="text" class="form-control" rows="5" name="name" required>
        </div>
        <div class="form-group">
          <label for="comment">Content<span style="color: red">*</span> :</label>
          <textarea class="form-control" rows="5" name="content" required></textarea>
        </div>
        <button class="btn btn-secondary" type="submit">Create Post</button>
      </form>
    </div>
  </div>
{% endblock content %}

Thêm hàm get_absolute_url ở model để trở lại trang list posts sau khi tạo nhé:

# posts/models.py

...
class Post(models.Model):
  ...
  ...
  def __unicode__(self):
    return self.content
  
  def get_absolute_url(self):
    return reverse('posts:list-posts', kwargs={})

4.3 Update Post

Để update bài Post đã tạo, chúng ta lại bắt đầu từ views.py

# posts/views.py
...
from django.views.generic import ListView, CreateView, UpdateView
from django.core.urlresolvers import reverse
...
class UpdatePostView(SuccessMessageMixin, UpdateView):
  template_name = 'posts/edit-post.html'
  model = Post
  fields = ['name', 'content',]
  success_message = 'Update Post successfully!'

  def get_success_url(self): 
    return reverse('posts:list-posts', kwargs={})

urls:

# posts/urls.py

...
from posts.views import (
  ListPostView,
  CreatePostView,
  UpdatePostView,
)

urlpatterns = [
    url(r'^list-posts/$', ListPostView.as_view(), name='list-posts'),
    url(r'^create-post/$', CreatePostView.as_view(), name='create-post'),
    url(r'^update-post/(?P<pk>[-\w]+)$', UpdatePostView.as_view(), name='update-post'),
]

templates:

<!-- posts/templates/posts/edit-post.html -->

{% extends "layouts/layout.html" %}
{% block title %}
    Edit Post | {{ block.super }}
{% endblock title %}
{% block content %}
  <h4><small>Edit Posts</small></h4>
  <hr>
  <div class="card-body">
    <div class="table-responsive">
      <form action="{% url 'posts:update-post' object.id %}" method="POST">
        {% csrf_token %}
        <div class="form-group">                    
          <label for="comment">Name<span style="color: red">*</span> :</label>
          <input type="text" class="form-control" rows="5" name="name" value="{{object.name}}" required>
        </div>
        <div class="form-group">
          <label for="comment">Content<span style="color: red">*</span> :</label>
          <textarea class="form-control" rows="5" name="content" required>{{object.content}}</textarea>
        </div>
        <button class="btn btn-secondary" type="submit">Update Post</button>
      </form>
    </div>
  </div>
{% endblock content %}

Sửa thêm nút edit vào trang list posts:

<!-- posts/templates/posts/list-posts.html -->
...
{% for post in posts %}
  <h2>{{post.name}}</h2>
  <h5><span class="glyphicon glyphicon-time"></span> Post at {{post.timestamp}}</h5>
  <p>{{post.content}}</p>
  <a href="{% url 'posts:update-post' post.id %}">Edit</a>
  <br>
{% endfor %}
...

4.4 Delete Post

Đối với chức năng xóa bài post, ở ví dụ này mình sẽ sử dụng Base View:

# posts/views.py
...
def delete_post(request, pk):
    post = Post.objects.filter(id=pk)
    post.delete()
    context = {
      "messages": "Delete Post successfully",
      'posts': Post.objects.all()
    }
    return render(request, 'posts/list-posts.html', context)

urls:

# posts/urls.py

...
from posts import views
...
urlpatterns = [
    url(r'^list-posts/$', ListPostView.as_view(), name='list-posts'),
    url(r'^create-post/$', CreatePostView.as_view(), name='create-post'),
    url(r'^update-post/(?P<pk>[-\w]+)$', UpdatePostView.as_view(), name='update-post'),
    url(r'^delete-post/(?P<pk>[-\w]+)$', views.delete_post, name='delete-post'),
]

Thêm nút xóa:

<!-- posts/list-posts.html -->

{% for post in posts %}
  <h2>{{post.name}}</h2>
  <h5><span class="glyphicon glyphicon-time"></span> Post at {{post.timestamp}}</h5>
  <p>{{post.content}}</p>
  <a href="{% url 'posts:update-post' post.id %}">Edit</a>
  <a href="{% url 'posts:delete-post' post.id %}" onclick="return confirm('Are you sure you want to delete this post?');">Delete</a>
  <br>
{% endfor %}

Hiển thị thông báo xóa thành công ở layout:

<!-- posts/templates/layouts/layout.html -->

<ul class="nav nav-pills nav-stacked">
  {% if messages %}
    {% for message in messages %}
    <div class="alert alert-success">
        <strong>{{ message }}</strong>
    </div>
    {% endfor %}
  {% endif %}
  {% if success %}
    <div class="alert alert-success">
        <strong>{{success}}</strong>
    </div>
  {% endif %}
  <li {% if request.path == "/posts/list-posts/" %}class="active"{% endif %}>
    <a href="{% url 'posts:list-posts' %}">List Posts</a>
  </li>
  <li {% if request.path == "/posts/create-post/" %}class="active"{% endif %}>
    <a href="{% url 'posts:create-post' %}">Create Posts</a>
  </li>
</ul><br>

5. DEMO

  • Create Post

    Kết nối django với mysql

    Khi success

    Kết nối django với mysql

  • Edit post

    Kết nối django với mysql

    Success

    Kết nối django với mysql

  • Delete post

    Kết nối django với mysql
    Success
    Kết nối django với mysql

6. End

Như vậy là chúng ta đã hoàn thành chủ đề CRUD cơ bản với Python sử dụng Django và MySql rồi.

Hy vọng bài viết này sẽ một phần nào đó giúp các bạn hiểu hơn về python.

Nếu thấy hay, hãy upvote, share để được đẹp trai và xinh gái hơn.