• Что бы вступить в ряды "Принятый кодер" Вам нужно:
    Написать 10 полезных сообщений или тем и Получить 10 симпатий.
    Для того кто не хочет терять время,может пожертвовать средства для поддержки сервеса, и вступить в ряды VIP на месяц, дополнительная информация в лс.

  • Пользаватели которые будут спамить, уходят в бан без предупреждения. Спам сообщения определяется администрацией и модератором.

  • Гость, Что бы Вы хотели увидеть на нашем Форуме? Изложить свои идеи и пожелания по улучшению форума Вы можете поделиться с нами здесь. ----> Перейдите сюда
  • Все пользователи не прошедшие проверку электронной почты будут заблокированы. Все вопросы с разблокировкой обращайтесь по адресу электронной почте : info@guardianelinks.com . Не пришло сообщение о проверке или о сбросе также сообщите нам.

Building a Service Marketplace with Django: Lessons from Netfix

Lomanu4 Оффлайн

Lomanu4

Команда форума
Администратор
Регистрация
1 Мар 2015
Сообщения
1,481
Баллы
155
As developers, we're constantly searching for the right tools to bring our ideas to life efficiently. When I decided to build Netfix, a service marketplace connecting companies with customers, I chose Django - and discovered features that make it uniquely powerful for complex applications.
In this article, I'll share deep technical insights from building Netfix, highlighting Django features that aren't commonly covered in tutorials but can dramatically improve your development workflow

Project Overview: Netfix


Netfix is a platform where:

  • Companies create profiles and list their services
  • Customers browse and request services
  • Each entity has personalized dashboards
  • Analytics track and display trending services
  • The system handles complex relationships between users, services, and requests

Let's dive into some of the most powerful features I leveraged

1. Custom User Models - The Right Way


Django's default user model works for simple applications, but for Netfix, I needed to support different user types with specific attributes. Here's how I implemented a custom user model:


from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager
from django.db import models

class UserManager(BaseUserManager):
def create_user(self, email, password=None, **extra_fields):
if not email:
raise ValueError('Email is required')
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user

def create_superuser(self, email, password=None, **extra_fields):
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
return self.create_user(email, password, **extra_fields)

class User(AbstractBaseUser, PermissionsMixin):
USER_TYPE_CHOICES = (
('customer', 'Customer'),
('company', 'Company'),
)

email = models.EmailField(unique=True)
name = models.CharField(max_length=255)
user_type = models.CharField(max_length=10, choices=USER_TYPE_CHOICES)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
date_joined = models.DateTimeField(auto_now_add=True)

objects = UserManager()

USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['name', 'user_type']

def __str__(self):
return self.email

Then I created separate profile models for each user type:


# accounts/models.py
class CompanyProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='company_profile')
company_name = models.CharField(max_length=255)
description = models.TextField()
logo = models.ImageField(upload_to='company_logos/', null=True, blank=True)
website = models.URLField(null=True, blank=True)

def __str__(self):
return self.company_name

class CustomerProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='customer_profile')
phone_number = models.CharField(max_length=20, null=True, blank=True)
address = models.TextField(null=True, blank=True)

def __str__(self):
return self.user.name

Pro tip: When creating a custom user model, do it at the beginning of your project. Changing the user model mid-project is extremely challenging.

2. Signals for Automatic Profile Creation


One feature I love about Django is signals. They allow you to trigger actions when certain events occur. I used signals to automatically create appropriate profiles when a user registers:


from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import User, CompanyProfile, CustomerProfile

@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
if instance.user_type == 'company':
CompanyProfile.objects.create(user=instance)
elif instance.user_type == 'customer':
CustomerProfile.objects.create(user=instance)

@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
if instance.user_type == 'company':
instance.company_profile.save()
elif instance.user_type == 'customer':
instance.customer_profile.save()
3. Advanced QuerySets with Annotations for Service Analytics


For the trending services feature, I needed to count service requests and display them by popularity. Django's ORM provides powerful annotation capabilities:


from django.db.models import Count, F, ExpressionWrapper, fields
from django.db.models.functions import Now, ExtractDay
from datetime import timedelta
from .models import Service, ServiceRequest

def trending_services(request):
# Calculate trending services based on request count in last 30 days
thirty_days_ago = timezone.now() - timedelta(days=30)

trending = Service.objects.annotate(
request_count=Count('servicerequest',
filter=models.Q(servicerequest__created_at__gte=thirty_days_ago))
).order_by('-request_count')[:10]

return render(request, 'services/trending.html', {'trending_services': trending})
4. Custom Template Tags for Dynamic UI Elements


One lesser-known Django feature is custom template tags. I created a template tag to display service status with appropriate styling:


from django import template
from django.utils.safestring import mark_safe

register = template.Library()

@register.filter
def status_badge(status):
colors = {
'pending': 'warning',
'accepted': 'info',
'in_progress': 'primary',
'completed': 'success',
'declined': 'danger'
}
color = colors.get(status, 'secondary')
return mark_safe(f'<span class="badge bg-{color}">{status.replace("_", " ").title()}</span>')

Then in templates:


{% load service_extras %}

<td>{{ service_request.status|status_badge }}
5. Class-Based Views with Mixins for Controlled Access


from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.views.generic import ListView, DetailView, CreateView, UpdateView

class CompanyRequiredMixin(UserPassesTestMixin):
def test_func(self):
return self.request.user.is_authenticated and self.request.user.user_type == 'company'

class ServiceCreateView(LoginRequiredMixin, CompanyRequiredMixin, CreateView):
model = Service
fields = ['name', 'description', 'price', 'category']
template_name = 'services/service_form.html'

def form_valid(self, form):
form.instance.company = self.request.user.company_profile
return super().form_valid(form)
6. Using Select Related and Prefetch Related for Performance


One challenge with relational data is the "N+1 query problem." Django's select_related and prefetch_related provide elegant solutions:


# Without optimization - this causes N+1 queries
def service_requests(request):
requests = ServiceRequest.objects.filter(service__company__user=request.user)
# Each time we access requests.service, we make a new query
return render(request, 'services/requests.html', {'requests': requests})

# With optimization
def service_requests_optimized(request):
requests = ServiceRequest.objects.filter(
service__company__user=request.user
).select_related(
'service', 'customer'
).prefetch_related(
'service__category'
)
# Now all related data is fetched in just 3 queries
return render(request, 'services/requests.html', {'requests': requests})
7. REST Framework ViewSets for API Development


To provide a robust API for third-party integrations and future platform extensibility, I built a comprehensive API using Django REST Framework's ViewSets:


from rest_framework import viewsets, permissions
from .serializers import ServiceSerializer, ServiceRequestSerializer
from services.models import Service, ServiceRequest

class IsCompanyOrReadOnly(permissions.BasePermission):
def has_permission(self, request, view):
if request.method in permissions.SAFE_METHODS:
return True
return request.user.is_authenticated and request.user.user_type == 'company'

class ServiceViewSet(viewsets.ModelViewSet):
serializer_class = ServiceSerializer
permission_classes = [IsCompanyOrReadOnly]

def get_queryset(self):
queryset = Service.objects.all()
category = self.request.query_params.get('category', None)
if category:
queryset = queryset.filter(category__slug=category)
return queryset

def perform_create(self, serializer):
serializer.save(company=self.request.user.company_profile)
8. Advanced Form Processing with FormSets


For services with multiple attributes, I used Django's formsets:


from django.forms import inlineformset_factory
from .models import Service, ServiceAttribute

def service_create_with_attributes(request):
AttributeFormSet = inlineformset_factory(
Service, ServiceAttribute,
fields=('name', 'value'),
extra=3, can_delete=True
)

if request.method == 'POST':
form = ServiceForm(request.POST)
if form.is_valid():
service = form.save(commit=False)
service.company = request.user.company_profile
service.save()

formset = AttributeFormSet(request.POST, instance=service)
if formset.is_valid():
formset.save()
return redirect('service-detail', pk=service.pk)
else:
form = ServiceForm()
formset = AttributeFormSet()

return render(request, 'services/service_with_attributes.html', {
'form': form,
'formset': formset
})
9. Leveraging Django Admin for Quick Internal Tools


Django's admin site is incredibly powerful. I customized it for internal management:



from django.contrib import admin
from .models import Service, ServiceRequest, Category

@admin.register(Service)
class ServiceAdmin(admin.ModelAdmin):
list_display = ('name', 'company', 'price', 'category', 'is_active')
list_filter = ('is_active', 'category', 'company')
search_fields = ('name', 'description', 'company__company_name')

def get_queryset(self, request):
qs = super().get_queryset(request)
if request.user.is_superuser:
return qs
return qs.filter(company__user=request.user)

@admin.register(ServiceRequest)
class ServiceRequestAdmin(admin.ModelAdmin):
list_display = ('service', 'customer', 'status', 'created_at')
list_filter = ('status', 'created_at')
date_hierarchy = 'created_at'
10. Middleware for Request Tracking


from .models import Service, ServiceView
from django.utils import timezone

class ServiceViewMiddleware:
def __init__(self, get_response):
self.get_response = get_response

def __call__(self, request):
response = self.get_response(request)

# Check if this is a service detail page
path_parts = request.path.strip('/').split('/')
if len(path_parts) >= 3 and path_parts[0] == 'services' and path_parts[1] == 'detail':
try:
service_id = int(path_parts[2])
service = Service.objects.get(id=service_id)

# Record anonymous view
ServiceView.objects.create(
service=service,
ip_address=self.get_client_ip(request),
user=request.user if request.user.is_authenticated else None,
viewed_at=timezone.now()
)
except (ValueError, Service.DoesNotExist):
pass

return response

def get_client_ip(self, request):
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0]
else:
ip = request.META.get('REMOTE_ADDR')

Don't forget to add it to your settings:


MIDDLEWARE = [
# ... other middleware
'services.middleware.ServiceViewMiddleware',
]
Conclusion


Building Netfix with Django taught me how powerful and flexible the framework truly is. These advanced features - from custom user models to complex queries with annotations - enabled me to create a robust marketplace with clean, maintainable code.
What I appreciate most about Django is how it scales with your knowledge. As you discover more features, you can refactor and improve your application while maintaining backward compatibility


Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.

 
Вверх Снизу