@mkone112
Начинающий питонист.

Как заменить сущности Django на сущности DRF, и имеет ли это смысл?

У меня есть небольшой pet-проект..
├── bookmarks
│  └── apps/
│    ├── account/
│    │  ├── forms.py
...
from .models import Profile


class LoginForm(forms.Form):
    username = forms.CharField()
    password = forms.CharField(widget=forms.PasswordInput)


class UserEditForm(forms.ModelForm):
    class Meta:
        model = User
        fields = ('first_name', 'last_name', 'email')


class ProfileEditForm(forms.ModelForm):
    class Meta:
        model = Profile
        fields = ('date_of_birth', 'photo')


class UserRegistrationForm(forms.ModelForm):
    password = forms.CharField(label='Password', widget=forms.PasswordInput)
    password_confirm = forms.CharField(
        label='Repeat password',
        widget=forms.PasswordInput
    )

    class Meta:
        model = User
        fields = ('username', 'first_name', 'email')

    def clean_password_confirm(self):
        cd = self.cleaned_data
        if cd['password'] != cd['password_confirm']:
            raise forms.ValidationError("Passwords don't match.")
        return cd['password_confirm']
│    │  ├── models.py
...
from .services.utils import get_random_default_profile_photo


class Profile(models.Model):
    user = models.OneToOneField(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
    )

    date_of_birth = models.DateField(blank=True, null=True)

    photo = models.ImageField(
        upload_to='media/users/%Y/%m/%d',
        default=get_random_default_profile_photo,
        blank=True,
        storage=FileSystemStorage(location=str(settings.BASE_DIR), base_url='/'),
    )

    def __str__(self):
        return f'Profile for user {self.user.username}'
│    │  ├── urls.py
...
from . import views

urlpatterns = [
    path('', views.dashboard, name='dashboard'),
    path('', include('django.contrib.auth.urls')),
    path('register/', views.register, name='register'),
    path('edit/', views.edit, name='edit'),
]
│    │  ├── views.py
...
from .forms import UserRegistrationForm, UserEditForm, ProfileEditForm
from .models import Profile


@login_required
def edit(request):
    if request.method == 'POST':
        user_form = UserEditForm(instance=request.user, data=request.POST)
        profile_form = ProfileEditForm(
            instance=request.user.profile,
            data=request.POST,
            files=request.FILES
        )
        if user_form.is_valid() and profile_form.is_valid():
            user_form.save()
            profile_form.save()
            messages.success(request, 'Profile updated successfully')
        else:
            messages.error(request, 'Error updating your profile')
    else:
        user_form = UserEditForm(instance=request.user)
        profile_form = ProfileEditForm(instance=request.user.profile)
    return render(
        request,
        'account/edit.html',
        {'user_form': user_form, 'profile_form': profile_form}
    )


def register(request):
    if request.method == 'POST':
        user_form = UserRegistrationForm(request.POST)
        if user_form.is_valid():
            new_user = user_form.save(commit=False)
            new_user.set_password(user_form.cleaned_data['password'])
            new_user.save()
            Profile.objects.create(user=new_user)

            return render(
                request,
                'account/register_done.html',
                {'new_user': new_user}
            )
    else:
        user_form = UserRegistrationForm()
    return render(request, 'account/register.html', {'user_form': user_form})
│    │  ...
│    └── images/
│        ├── forms.py
...
from .models import Image


class ImageCreateForm(forms.ModelForm):
    class Meta:
        model = Image
        fields = ['title', 'url', 'description']
        widgets = {'url': forms.HiddenInput}

    def clean_url(self):
        url = self.cleaned_data['url']
        valid_suffixes = ('.jpg', '.jpeg', '.png', '.gif')
        image_suffix = Path(url).suffix

        if image_suffix not in valid_suffixes:
            raise forms.ValidationError(
                f'The given URL({url}) does not match valid image extensions!'
            )

        return url

    def save(self, force_insert=False, force_update=False, commit=True):
        url = self.cleaned_data['url']
        # Get an image object.
        response = requests.get(url)
        image = super().save(commit=False)
        image_slug = slugify(image.title)
        filename_with_extension = image_slug + Path(url).suffix
        image.image.save(
            filename_with_extension,
            ContentFile(response.content),
            save=False
        )
        if commit:
            image.save()
        return image
│        ├── models.py
...
class Image(models.Model):
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        related_name='images_created',
        on_delete=models.CASCADE
    )

    users_liked = models.ManyToManyField(
        settings.AUTH_USER_MODEL,
        through='Like',
        related_name='images_liked',
        blank=True
    )

    image = models.ImageField(upload_to='images/%Y/%m/%d/')

    title = models.CharField(max_length=200)
    description = models.TextField(blank=True)
    url = models.URLField(max_length=2048)
    slug = models.SlugField(max_length=200, blank=True)
    created = models.DateField(auto_now_add=True, db_index=True)

    def __str__(self):
        return self.title

    def save(self, *args, **kwargs):
        if not self.slug:
            self.slug = slugify(self.title)
        super().save(*args, **kwargs)

    def get_absolute_url(self):
        return reverse('images:detail', args=(self.id, self.slug))

    def last_liked(self):
        return self.users_liked.order_by('like__liked_at')


class Like(models.Model):
    image = models.ForeignKey(Image, on_delete=models.CASCADE)
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    liked_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ('liked_at',)
│        ├── urls.py
...

urlpatterns = [
    path('create/', views.image_create, name='create'),
    path('detail/<int:id>/<slug:slug>/', views.image_detail, name='detail'),
    path('like/', views.image_like, name='like'),
    path('', views.images_list, name='list'),
]
│        ├── views.py
...

from .forms import ImageCreateForm
from .models import Image
from bookmarks.services.decorators import ajax_required


@login_required
def image_create(request):
    if request.method == 'POST':
        form = ImageCreateForm(request.POST)
        if form.is_valid():
            new_image = form.save(commit=False)
            new_image.user = request.user
            new_image.save()
            messages.success(request, 'Image added successfully')

            return redirect(new_image.get_absolute_url())
    else:
        form = ImageCreateForm(request.GET)

    return render(
        request,
        'images/image/create.html',
        {
            'section': 'images',
            'form': form,
        }
    )


def image_detail(request, id, slug):
    image = get_object_or_404(Image, id=id, slug=slug)

    return render(
        request,
        'images/image/detail.html',
        {
            'section': 'images',
            'image': image,
        }
    )


@ajax_required
@login_required
@require_POST
def image_like(request):
    image_id = request.POST.get('image-id')
    action = request.POST.get('action')
    if image_id and action:
        try:
            image = Image.objects.get(id=image_id)
        except Model.DoesNotExist:
            return JsonResponse({'status': 'ImageDoesNotExist'})

        if action == 'like':
            image.users_liked.add(request.user)
        elif action == 'unlike':
            image.users_liked.remove(request.user)

        return JsonResponse({'status': 'ok'})

    return JsonResponse({'status': 'Bad request'})


@login_required
def images_list(request):
    page = request.GET.get('page')
    all_images = Image.objects.all()
    paginator_object = paginator.Paginator(all_images, settings.IMAGES_ON_PAGE)
    try:
        images = paginator_object.page(page)
    except paginator.PageNotAnInteger:
        images = paginator_object.page(1)
    except paginator.EmptyPage:
        if request.is_ajax():
            return HttpResponse('')

        images = paginator_object.page(paginator_object.num_pages)

    if request.is_ajax():
        template_name = 'images/image/list_ajax.html'
    else:
        template_name = 'images/image/list.html'

    return render(
        request,
        template_name,
        {
            'section': 'images',
            'images': images,
        }
    )
...        ...
Код:
  • Далек от лаконичности.
  • Часто содержит логику вида:
    if POST:
        сериализуем объект
        рендерим страницу с сообщением об успехе
    else:
        рендерим страницу с формой

  • Вывод, создание, изменение одной сущности выполняет множество отдельных views.

При чтении доков DRF мне показалось что он - решение.
  • Заменить формы на сериализаторы, выполнять сериализацию в них и при необходимости рендерить в формы.
  • Заменить view's на ViewSet's.
  • Вместо urlpatterns использовать DefaultRouter.

Но при попытке реализации - я не осилил даже логин через форму.

Вопрос - я упролся? DRF вообще подходит для такого? Где можно посмотреть примеры реализации?
  • Вопрос задан
  • 141 просмотр
Решения вопроса 1
tumbler
@tumbler
бекенд-разработчик на python
DRF - он про API, а ваши Django-вьюхи про серверную генерацию и обработку форм. Назначение одно и то же, но реализация существенно различается.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
repair-cat
@repair-cat
возможно здесь вы найдете ответ
https://www.django-rest-framework.org/topics/html-...
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы