갈루아의 반서재

LEMP 기반으로 우분투에 설치된 워드프레스를 Django 프레임워크와 연동하여 관리해보자. 본 예제에 앞서 진행되어야 할 관련 포스팅은 아래 내용을 참고한다. 


장고 설치 및 워드프레스 DB 연결


먼저 장고를 설치한다. 금일 기준 최신 버전은 Django 2.1.3 이다. 

1
2
3
4
5
6
7
8
9
10
(dominika) founder@hilbert:~$ pip install Django==2.1.3
Collecting Django==2.1.3
  Downloading https://files.pythonhosted.org/packages/d1/e5/2676be45ea49cfd09a663f289376b3888accd57ff06c953297bfdee1fb08/Django-2.1.3-py3-none-any.whl (7.3MB)
    100|████████████████████████████████| 7.3MB 2.4MB/s
Collecting pytz (from Django==2.1.3)
  Downloading https://files.pythonhosted.org/packages/f8/0e/2365ddc010afb3d79147f1dd544e5ee24bf4ece58ab99b16fbb465ce6dc0/pytz-2018.7-py2.py3-none-any.whl (506kB)
    100|████████████████████████████████| 512kB 21.0MB/s
Installing collected packages: pytz, Django
Successfully installed Django-2.1.3 pytz-2018.7
 
cs


장고가 제대로 설치되었는지 확인하고 새로운 프로젝트를 생성하자.

1
2
3
4
5
6
7
8
9
10
11
(dominika) founder@hilbert:~$ python -m django --version
2.1.3
 
(dominika) founder@hilbert:~$ mkdir teamgalois
(dominika) founder@hilbert:~$ cd teamgalois
(dominika) founder@hilbert:~/teamgalois$ django-admin startproject teamgalois
(dominika) founder@hilbert:~/teamgalois$ ls -al
total 12
drwxrwxr-x  3 founder founder 4096 Nov 10 08:29 .
drwxr-xr-15 founder founder 4096 Nov 10 08:29 ..
drwxrwxr-x  3 founder founder 4096 Nov 10 08:29 teamgalois
cs


호스트를 추가하고, 8000 포트로 장고 서비스를 실행한다. 

/home/founder/teamgalois/teamgalois/settings.py

1
2
3
4
 
ALLOWED_HOSTS = ['www.teamgalois.com']
 
 
cs


1
2
3
4
5
6
7
8
9
10
11
(dominika) founder@hilbert:~$ django-admin startproject teamgalois
(dominika) founder@hilbert:~$ cd teamgalois
(dominika) founder@hilbert:~/teamgalois$ python manage.py runserver 0.0.0.0:8000
Performing system checks...
 
System check identified no issues (0 silenced).
 
November 102018 - 08:53:20
Django version 2.1.3, using settings 'teamgalois.settings'
Starting development server at http://0.0.0.0:8000/
Quit the server with CONTROL-C.
cs


정상적으로 설치가 되었다.


워드프레스의 MySQL 접속 정보를 설정 파일에 입력한다.

/home/founder/teamgalois/teamgalois/settings.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 
try:
    import pymysql
    pymysql.install_as_MySQLdb()
except:
    pass
    
DATABASES = {
    'default': {
        'ENGINE''django.db.backends.mysql'
        'NAME''mysql',
        'USER''*******',
        'PASSWORD''************',
        'HOST''localhost',   # Or an IP Address that your DB is hosted on
        'PORT''3306',
    }
}
 
cs

아래와 같은 오류 메시지가 뜬다.

1
2
3
4
5
6
 
 
django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module.
Did you install mysqlclient?
 
 
cs

참조링크 Error loading MySQLdb Module 'Did you install mysqlclient or MySQL-python?'

링크의 설명대로 pymysql 을 설치하고, __init__.py 파일에 아래 내용을 추가한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
~/teamgalois$ pip install pymysql
Collecting pymysql
  Downloading https://files.pythonhosted.org/packages/a7/7d/682c4a7da195a678047c8f1c51bb7682aaedee1dca7547883c3993ca9282/PyMySQL-0.9.2-py2.py3-none-any.whl (47kB)
    100|████████████████████████████████| 51kB 1.0MB/s
Collecting cryptography (from pymysql)
  Downloading https://files.pythonhosted.org/packages/ec/18/1583e40c38ff8572c42e56ce17b95357a9ebb91375cfbd7aad63cac9a32e/cryptography-2.4.1-cp34-abi3-manylinux1_x86_64.whl (2.1MB)
    100|████████████████████████████████| 2.1MB 10.6MB/s
Collecting idna>=2.1 (from cryptography->pymysql)
  Downloading https://files.pythonhosted.org/packages/4b/2a/0276479a4b3caeb8a8c1af2f8e4355746a97fab05a372e4a2c6a6b876165/idna-2.7-py2.py3-none-any.whl (58kB)
    100|████████████████████████████████| 61kB 18.4MB/s
Requirement already satisfied: six>=1.4.1 in /home/founder/anaconda3/envs/dominika/lib/python3.7/site-packages (from cryptography->pymysql) (1.11.0)
Collecting asn1crypto>=0.21.0 (from cryptography->pymysql)
  Downloading https://files.pythonhosted.org/packages/ea/cd/35485615f45f30a510576f1a56d1e0a7ad7bd8ab5ed7cdc600ef7cd06222/asn1crypto-0.24.0-py2.py3-none-any.whl (101kB)
    100|████████████████████████████████| 102kB 23.0MB/s
Collecting cffi!=1.11.3,>=1.7 (from cryptography->pymysql)
  Downloading https://files.pythonhosted.org/packages/51/7b/d1014289d0578c3522b2798b9cb87c65e5b36798bd3ae68a75fa1fe09e78/cffi-1.11.5-cp37-cp37m-manylinux1_x86_64.whl (421kB)
    100|████████████████████████████████| 430kB 4.1MB/s
Collecting pycparser (from cffi!=1.11.3,>=1.7->cryptography->pymysql)
  Downloading https://files.pythonhosted.org/packages/68/9e/49196946aee219aead1290e00d1e7fdeab8567783e83e1b9ab5585e6206a/pycparser-2.19.tar.gz (158kB)
    100|████████████████████████████████| 163kB 27.4MB/s
Building wheels for collected packages: pycparser
  Running setup.py bdist_wheel for pycparser ... done
  Stored in directory: /home/founder/.cache/pip/wheels/f2/9a/90/de94f8556265ddc9d9c8b271b0f63e57b26fb1d67a45564511
Successfully built pycparser
Installing collected packages: idna, asn1crypto, pycparser, cffi, cryptography, pymysql
Successfully installed asn1crypto-0.24.0 cffi-1.11.5 cryptography-2.4.1 idna-2.7 pycparser-2.19 pymysql-0.9.2
cs

/home/founder/teamgalois/teamgalois/__init__.py

1
2
3
import pymysql
 
pymysql.install_as_MySQLdb()
cs


다시 장고 서비스를 실행해보자. 이번에는 접근 오류가 발생한다. 

1
2
3
4
5
6
7
8
9
 
    packet.check_error()
  File "/home/founder/anaconda3/envs/dominika/lib/python3.7/site-packages/pymysql/protocol.py", line 220, in check_error
    err.raise_mysql_exception(self._data)
  File "/home/founder/anaconda3/envs/dominika/lib/python3.7/site-packages/pymysql/err.py", line 109, in raise_mysql_exception
    raise errorclass(errno, errval)
django.db.utils.OperationalError: (1045"Access denied for user 'jennifer'@'localhost' (using password: YES)")
 
 
cs

위의 MySQL ERROR 1045 접근 거부는 제타 워키 설명에 나와있는대로, MySQL 패스워드가 맞지 않는 것이다. 그런데 MySQL의 경우 동일한 계정이라도, 접속지에 따라 서로 다른 패스워드를 부여할 수 있으니, 이 부분까지 체크해서 settings.py 파일을 수정한다.

일단 여기까지는 Database 셋팅의 default 값을 워드프레스 DB로 대체하여 정상적으로 접속이 되는지를 확인했다. 다음에는 db 라우팅을 통해 추가해보도록 하자.


참고로 mysql-connector-python 과 mysqlclient 어떤 점에서 차이가 나는지는 아래 링크를 참조한다.


레거시 데이터베이스와 장고 통합하기


진행에 앞서 도움이 될만한 몇 가지 링크를 챙겨보자. 


설치된 앱에 teamgalois 를 추가하고, 장고 생성시 기본적으로 만들어지는 데이터베이스는 그냥 두고, 워드프레스 접속 정보를 추가한다. 그리고 라우터 파일을 수정하여 모델의 라벨링에 따라 접속할 데이터베이스를 지정한다. 

/home/founder/teamgalois/teamgalois/settings.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'teamgalois',
]
 
try:
    import pymysql
    pymysql.install_as_MySQLdb()
except:
    pass
 
DATABASE_ROUTERS = ['teamgalois.routers.DatabaseAppsRouter']
DATABASE_APPS_MAPPING = {'contenttypes''default',
                         'auth''default',
                         'admin''default',
                         'sessions''default',
                         'messages''default',
                         'staticfiles''default',
                         'teamgalois''wp',
                        }
    
DATABASES = {
    'default': {
        'ENGINE''django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    },
    'wp': {
        'ENGINE''django.db.backends.mysql'
        'NAME''redsparrow',
        'USER''**************',
        'PASSWORD''*****************',
        'HOST''localhost',   # Or an IP Address that your DB is hosted on
        'PORT''3306',
    }
}
 
cs


/home/founder/teamgalois/teamgalois/routers.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
from django.conf import settings
class DatabaseAppsRouter(object):
    """
    A router to control all database operations on models in the
    user application.
    """
    def db_for_read(self, model, **hints):
        """
        Attempts to read user models go to users_db.
        """
        if model._meta.app_label == 'teamgalois':
            return 'wp'
        return None
 
    def db_for_write(self, model, **hints):
        """
        Attempts to write user models go to users_db.
        """
        if model._meta.app_label == 'teamgalois':
            return 'wp'
        return None
 
    def allow_relation(self, obj1, obj2, **hints):
        """
        Allow relations if a model in the user app is involved.
        """
        if obj1._meta.app_label == 'teamgalois' or \
           obj2._meta.app_label == 'teamgalois':
           return True
        return None
 
    def allow_migrate(self, db, app_label, model_name=None, **hints):
        """
        Make sure the auth app only appears in the 'users_db'
        database.
        """
        if app_label == 'teamgalois':
            return db == 'wp'
        return None
 
cs


여기까지 한 후 inspectdb 를 통해서 장고 모델을 자동으로 생성한다. 

1
~/teamgalois$ python manage.py inspectdb --database=wp > teamgalois/models.py
cs

아래와 같이 생성이 되었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# This is an auto-generated Django model module.
# You'll have to do the following manually to clean this up:
#   * Rearrange models' order
#   * Make sure each model has one field with primary_key=True
#   * Make sure each ForeignKey has `on_delete` set to the desired behavior.
#   * Remove `managed = False` lines if you wish to allow Django to create, modify, and delete the table
# Feel free to rename the models, but don't rename db_table values or field names.
from django.db import models
 
 
class WpBpActivity(models.Model):
    id = models.BigAutoField(primary_key=True)
    user_id = models.BigIntegerField()
    component = models.CharField(max_length=75)
    type = models.CharField(max_length=75)
    action = models.TextField()
    content = models.TextField()
    primary_link = models.TextField()
    item_id = models.BigIntegerField()
    secondary_item_id = models.BigIntegerField(blank=True, null=True)
    date_recorded = models.DateTimeField()
    hide_sitewide = models.IntegerField(blank=True, null=True)
    mptt_left = models.IntegerField()
    mptt_right = models.IntegerField()
    is_spam = models.IntegerField()
 
    class Meta:
        managed = False
        db_table = 'wp_bp_activity'
 
cs


이제 워드프레스 테이블 데이터 조회 및 수정, 삭제 등이 정상적으로 되는지 확인하기 위해 장고 관리자를 생성하고, 장고 어드민에 테이블 하나를 추가해서 테스트를 진행하자. 아래에서 보다시피 이와 관련된 테이블이 마이그레이션 되지 않은 상태이다. 

1
2
django.db.utils.ProgrammingError: (1146"Table 'redsparrow.auth_user' doesn't exist")
 
cs

마이그레이션을 진행한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
~/teamgalois$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying sessions.0001_initial... OK
cs

그리고 관리자를 생성한다. 

1
2
3
4
5
6
~/teamgalois$ python manage.py createsuperuser
Username (leave blank to use 'root'): root
Email address: ***********@gmail.com
Password:
Password (again):
Superuser created successfully.
cs

관리자로 정상 로그인이 됨을 알 수 있다.

이제 admin.py 파일을 수정하여 데이터 조회, 생성, 삭제 등의 기능이 정상 작동하는지 확인하자. 

/home/founder/teamgalois/teamgalois/admin.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# -*- coding: utf-8 -*- 
from django.contrib import admin
# from django.contrib.admin import AdminSite
from django.contrib.admin.templatetags.admin_list import date_hierarchy
from teamgalois.models import WpComments
 
# class AdminBase(admin.ModelAdmin):
#     def natural_title(self, obj):
#         return unicode(obj)
#     natural_title.short_description = 'Title'
 
class AdminWpComments(admin.ModelAdmin):
 
    model = WpComments
     
    fieldsets = [
        (None,            {'fields': ['comment_post_id']}),
        (None,            {'fields': ['comment_author']}),
        (None,            {'fields': ['comment_author_ip']}),
        (None,            {'fields': ['comment_date']}),
        (None,            {'fields': ['comment_date_gmt']}),
        (None,            {'fields': ['comment_content']}),
        (None,            {'fields': ['comment_karma']}),
        (None,            {'fields': ['comment_approved']}),
        (None,            {'fields': ['comment_agent']}),
        (None,            {'fields': ['comment_parent']}),
        (None,            {'fields': ['user_id']}),
    ]
    list_display = ('comment_id''comment_post_id''comment_author','comment_date''comment_date_gmt'
                     ,'comment_karma','comment_approved','comment_parent','user_id' )
    search_fields = ['comment_content']
    ordering = ('-comment_id',)
 
admin.site.register(WpComments,AdminWpComments) 
cs

여기서 필드셋에 pk 인 comment_id 를 추가하게 되면 다음과 같은 OperationalError 가 발생하니 주의한다. 넣고 싶으면 readonly 처리를 해줘야한다.

1
          (None,            {'fields': ['comment_id']}), 
cs


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
OperationalError at /admin/teamgalois/wpcomments/
no such table: wp_comments
Request Method:    GET
Request URL:    http://www.teamgalois.com:8000/admin/teamgalois/wpcomments/
Django Version:    2.1.3
Exception Type:    OperationalError
Exception Value:    
no such table: wp_comments
Exception Location:    /home/founder/anaconda3/envs/dominika/lib/python3.7/site-packages/django/db/backends/sqlite3/base.py in execute, line 296
Python Executable:    /home/founder/anaconda3/envs/dominika/bin/python
Python Version:    3.7.1
Python Path:    
['/home/founder/teamgalois',
 '/home/founder/anaconda3/envs/dominika/lib/python37.zip',
 '/home/founder/anaconda3/envs/dominika/lib/python3.7',
 '/home/founder/anaconda3/envs/dominika/lib/python3.7/lib-dynload',
 '/home/founder/anaconda3/envs/dominika/lib/python3.7/site-packages']
Server time:    Mon, 12 Nov 2018 09:28:14 +0000
cs

이제 해당 테이블을 장고 관리자에서 확인할 수 있고, 삭제 등의 기능도 정상적으로 작동함을 알 수 있다.

phpMyAdmin 사이트에서도 해당 코멘트가 삭제되어 있음을 알 수 있다.