click fraud detection
0 800 755 007
(Безкоштовно по Україні)

Створення IM штор. Частина 2. Додаток Django

13883

Минулої статті ми розпочали розробку інтернет-магазину та завантажили з сайту-прикладу каталог із збереженням їх структури у файловій системі. Також ми завантажили список товарів для кожної категорії. Нам залишилося зайти на сторінку товарної позиції, скопіювати інформацію про товар та завантажити його зображення.

Погляньмо на те, як виглядає HTML-код позиції товару на сторінці категорії:

https://pangardin.com.ua/category/complect/artdeco/

Після одержання елемента li:

 products = soup.find('ul',{'class': 'products'}).findAll('li')

ми вибрали блок із класом inner_product_header і використовували заголовок для знаходження назви товару.

 title = product.find('div',{'class': 'inner_product_header'}).find('h3').text

Тепер необхідно отримати URL-адресу сторінки з описом товару.

 link = product.find('div',{"class": "inner_cart_button"}).find('a').get('href')

У цьому рядку ми спочатку знаходимо елемент div з класом inner_cart_button , а потім у ньому посилання, з якого атрибут href .

Завантаживши сторінку опису товару знайдемо елемент із зображенням у вихідному коді.

 

Отримати на нього посилання можна таким рядком коду, де ми шукаємо по класу MagicZoomPlus .

 img_link = soup.find('a',{"class": "MagicZoomPlus"}).get('href')

Залишилось завантажити зображення.

Створимо нову функцію save_position, що зберігає позицію товару, в яку передамо два параметри URL і шлях до каталогу товару. У цій функції ми отримуємо html-сторінку, знаходимо в ній посилання на зображення та завантажуємо його. Потім створюємо папку images, у якій зберігаємо зображення на диск.

 def save_position(url,path):
''' сохранение позиции товара '''
r = requests.get(url)
soup = BeautifulSoup(r.text, 'html.parser')
img_link = soup.find('a',{"class": "MagicZoomPlus"}).get('href')
print("Downloading pic %s" % img_link)
# забираем картинку
r = requests.get(img_link, stream=True)
# создаем каталог images
path_to_img = os.path.join(path,'images')
if not os.path.isdir(path_to_img):
os.mkdir(path_to_img)
# сохраняем картинку
image_full_path = os.path.join(path_to_img,'1.png')
if r.status_code == 200:
with open(image_full_path, 'wb') as f:
r.raw.decode_content = True
shutil.copyfileobj(r.raw, f)

Знайдемо заголовок товару та опис, зберігши ці дані у файлі meta.yml

 # находим название и описание
title = soup.find('h1',{"class": "product_title"}).text
description = soup.find('div',{"itemprop": "description"}).text
meta_info = '''
name_slug: %s
name_ru: %s
meta_title_ru: %s
meta_keywords_ru: %s
meta_description_ru: %s
is_published: true
description_ru: |
%s
''' % (slugify(title),title,title,title,title,description)
with open(os.path.join(path,'meta.yml'),'w') as f:
f.write(meta_info)

Для того, щоб отримати більш детальну та «чисту» інформацію про товар, знадобиться деяка вправність у роботі зі структурою HTML сторінки. Швидше за все, треба буде відсівати зайву інформацію та вибирати те, що вам потрібно для майбутнього сайту. Приклад нашого сайту-джерела написаний на Wordpress і для кожного нового сайту знадобиться написати свій власний парсер контенту.

Створення проекту Django та моделі програми

Створимо каталог віртуального оточення, де зберігатимемо наші залежності, включаючи Django .

 virtualenv -p python3 venv

Тепер після активації командою:

 . ./venv/bin/activate

можна встановити фреймворк Django.

 pip install django

Створюємо проект, міняємо директорію та запускаємо міграцію.

 django-admin.py startproject prj
cd prj
./manage.py migrate

У процесі міграції буде створено базу даних у новому файлі db.sqlite3 , яка містить таблиці вбудованих до набору постачання програм Django.

Далі створимо наш додаток:

 ./manage.py startapp shop

У цьому створюється директорія докладання із типовою структурою файлів.

models.py – для опису класів моделі (таблиць бази даних);

admin.py – для класів адмін-інтерфейсу;

views.py – у цьому файлі ми створюємо уявлення (функції або класи, що відповідають за генерацію веб-сторінок). Спрощено вважати їх контролерами;

tests.py - код з авто-тестами;

apps.py – налаштування програми.

Далі необхідно включити нашу програму в налаштуваннях проекту у файлі settings.py , додавши його назву до змінної списку INSTALLED_APPS .

 INSTALLED_APPS = [
'django.contrib.admin',
….
'django.contrib.staticfiles',
'shop'
]

Після чого опишемо нашу модель у файлі models.py , де визначимо структуру майбутніх таблиць бази даних та позначимо їх взаємозв'язки за допомогою зовнішніх ключів.

 from django.db import models

class Category(models.Model):
name = models.CharField(max_length=250)
name_slug = models.CharField(max_length=250)

class Subcategory(models.Model):
name = models.CharField(max_length=250)
name_slug = models.CharField(max_length=250)
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)

class Good(models.Model):
name = models.CharField(max_length=250)
name_slug = models.CharField(max_length=250)
desc = models.TextField()
subcategory = models.ForeignKey(Subcategory, on_delete=models.SET_NULL, null=True)

class Image(models.Model):
good = models.ForeignKey(Good, on_delete=models.SET_NULL, null=True)
image = models.ImageField()

Кожен клас моделі успадковується від класу models.Model та представляє таблицю. Атрибути класів - це поля таблиць із певним типом даних, значенням за умовчанням, розмірністю тощо. Зв'язки із зовнішніх ключів визначаються полями відповідного типу ForeignKey та прив'язуються до класів відповідних таблиць із зазначенням логіки поведінки при видаленні пов'язаного запису параметром on_delete .

Адмін-інтерфейс

Опишемо класи адмін-інтерфейсу та прив'яжемо їх до класів моделі у файлі shop/admin.py .

 from django.contrib import admin
from .models import *

class CategoryAdmin(admin.ModelAdmin):
pass

class SubcategoryAdmin(admin.ModelAdmin):
pass

class GoodAdmin(admin.ModelAdmin):
pass

class ImageAdmin(admin.ModelAdmin):
pass

admin.site.register(Category, CategoryAdmin)
admin.site.register(Subcategory,SubcategoryAdmin)
admin.site.register(Good, GoodAdmin)
admin.site.register(Image, ImageAdmin)

Ми визначили порожні класи адмінок для всіх таблиць. Вони дозволять вивести 4 адмін-розділи з налаштуваннями за умовчанням.

Однак нічого не заважає кастомізувати інтерфейс, визначивши спеціальні атрибути.

Наприклад, перерахувати поля для пошуку у списку можна так:

 search_fields = ['name', 'journal__name_ru']

Причому через подвійне підкреслення можна вказувати поля пошуку із залежних таблиць. Після визначення цього атрибута в адмін-панелі з'явиться поле для введення слова пошуку.

Сортування.

 ordering = ['name']

Фільтр.

 list_filter = ('is_public', 'company')

Список полів у загальному списку записів.

 list_display = ('name', 'is_public', 'company')

Виняток непотрібних полів із форми редагування.

 exclude = ['uuid_key']

Визначення полів у режимі лише читання.

 readonly_fields = ('reader_url', 'moderator' )

За допомогою атрибуту inlines можна встановити клас залежної таблиці для виведення записів, відповідних поточному елементу при редагуванні під формою.

 inlines = [IssuePageInline, ]

Щоб вивести картинки в адмін-інтерфейсі, визначимо змінну list_display , вказавши поле image_tag , яке пізніше створимо у моделі.

 class ImageAdmin(admin.ModelAdmin):
list_display = ['image', 'image_tag', 'good']

Додамо новий метод image_tag у клас моделі зображень, задекорувавши його декоратором @property щоб мати можливість посилатися на метод як на звичайну властивість при налаштуванні адмін-інтерфейсу.

 class Image(models.Model):
''' Изображения '''
good = models.ForeignKey(Good, on_delete=models.SET_NULL, null=True)
image = models.ImageField()

@property
def image_tag(self):
return mark_safe (' ' % self.image.url)

функцією mark_safe ми відключаємо екранування html-тегів.

Команда імпорту даних.

Створимо нову команду імпорту даних ( shop/management/commands/import.py ) .

    from django.core.management.base import BaseCommand, CommandError
from shop.models import *

class Command(BaseCommand):
def handle(self, *args, **options):
print('Importing data')

Не забуваймо створювати файли __ini__.py у нових каталогах management та commands.

Заповнимо метод handle , де прочитаємо каталог з даними та збережемо інформацію про каталоги та товарні позиції з файлової системи.

Код імпорту розбито на дві функції: import_goods та import_catalog , які імпортують товари та категорії відповідно.

Повний код команди та результат виконання наведено нижче:

 from django.core.management.base import BaseCommand, CommandError
from shop.models import *
from prj.settings import BASE_DIR
import os
import sys
import yaml
from django.core.files import File
DATA_DIR = os.path.join(BASE_DIR,'..','data','pangardin.com.ua')

def import_catalog():
# очищаем таблицы
Subcategory.objects.all().delete()
Category.objects.all().delete()
Good.objects.all().delete()
Image.objects.all().delete()
for item in os.listdir(DATA_DIR):
if os.path.isdir(os.path.join(DATA_DIR,item)):
# читаем meta.yml
with open(os.path.join(DATA_DIR,item,'meta.yml'),'r') as f:
rez = f.read()
# парсим yml формат
yml_data = yaml.load(rez)
print(yml_data['name_ru'])
# создаем категории в базе если такой нет
try:
cat = Category.objects.get(name_slug=yml_data['parent_slug'])
except:
cat = Category()

cat.name = yml_data['parent_name_ru']
cat.name_slug = yml_data['parent_slug']
cat.save()
# создаем подкатегории в базе если такой нет
try:
scat = Subcategory.objects.get(name_slug=yml_data['name_slug'])
except:
scat = Subcategory()
scat.name = yml_data['name_ru']
scat.name_slug = yml_data['name_slug']
scat.category = cat
scat.save()

for item_good in os.listdir(os.path.join(DATA_DIR,item)):
if os.path.isdir(os.path.join(DATA_DIR,item,item_good)):
import_goods(item_good,os.path.join(DATA_DIR,item),scat)

# функция импорта товаров

def import_goods(name_slug,path,sub_category):
print('Importing ..... %s' % name_slug)
# читаем meta.yml
try:
with open(os.path.join(path,name_slug,'meta.yml'),'r') as f:
rez = f.read()
except:
return False
# парсим yml формат
yml_data = yaml.load(rez)
# сохраняем позицию товара
g = Good()
g.name = yml_data['name_ru']
g.name_slug = yml_data['name_slug']
g.desc = yml_data['description_ru']
g.subcategory = sub_category
g.save()

# сохраняем картинки
path_to_image = os.path.join(path,name_slug,'images','1.png')
#print(path_to_image)

img = Image()
img.good = g
img.save()
file_name = '%s.png' % g.id
with open(path_to_image, 'rb') as image_file:
img.image.save(file_name,File(image_file),save=True)

class Command(BaseCommand):
def handle(self, *args, **options):
print('Importing data')
import_catalog()


Висновки

У цій статті ми розглянули процес створення програми на базі фреймворку Django. Розповіли про те, як створювати модель даних, створювати та кастомізувати адмін-інтерфейс для моделі даних. Також ми висвітлили процес створення консольної команди для заповнення бази даних інформацією із файлової системи, яка була раніше завантажена із сайту-прикладу.

У вас залишились запитання?

Залиште контактні дані. Наш менеджер зв'яжеться та проконсультує вас.

4.8/5
Корисність
Проголосували 5
Як вам стаття?
Давайте обговоримо Ваш проект
Давайте почнемо розмову!
КОМЕНТАРІ0
Можливо
CRM – інструмент для бізнесу, без якого компанії буде складно працювати з клієнтами. Він допомагає…
Євген Паталяк
Євген Паталяк
Бренд та бізнес мають розвиватися одночасно.
Галина Назарова
Галина Назарова
Мобільний додаток для автосервісу – потужний інструмент для роботи з клієнтами та збільшення можливостей бізнесу.
Wezom
Wezom
ПІДПИСУЙТЕСЯ НА РОЗСИЛКУ АЙТІБЛОГ
Бажаєте отримувати 
цікаві статті?