[PYTHON] Django: Record User Agent and manage with Admin

Project creation

--Get login form with GET / accounts / login --Login with POST / accounts / login and redirect to / accounts / profile / --Display personal information with GET / accounts / profile

startproject

(v17)hdknr@wzy:~/ve/v17/src$ mkdir -p ua/web
(v17)hdknr@wzy:~/ve/v17/src$ django-admin startproject app ua/web
(v17)hdknr@wzy:~/ve/v17/src$ cd ua/web
(v17)hdknr@wzy:~/ve/v17/src/ua/web$ python manage.py migrate
(v17)hdknr@wzy:~/ve/v17/src/ua/web$ python manage.py createsuperuser

Add accounts

(v17)hdknr@wzy:~/ve/v17/src/ua/web$ python manage.py startapp accounts

app/settings.py

INSTALLED_APPS += (
    'accounts',
)

app/urls.py

    url(r'^accounts/', include('accounts.urls')),

accounts/urls.py

from django.conf.urls import patterns, url
import views

urlpatterns = patterns(
    '',
    url(r'login/$', 'django.contrib.auth.views.login'),
    url(r'profile/$', views.profile),
)

accounts/views.py

from django.contrib.auth.decorators import login_required
from django.template.response import TemplateResponse

import models


@login_required
def profile(request):
    ua = None
    return TemplateResponse(
        request,
        'registration/profile.html',
        dict(request=request, ua=ua, ))

accounts/templates/registration

(v17)hdknr@wzy:~/ve/v17/src/ua/web$ mkdir -p accounts/templates/registration
<form method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" />
</form>
{{ request.user }}
{{ ua }}

--At this point, it works until login

UserAgent model

UA judgment (accounts / ua.py)

--agent_type (): Find a matching agent with a regular expression

import re


DETECTOR = [
    # Featured Phone
    r'(?P<agent>DoCoMo)',
    r'(?P<agent>SoftBank)',
    r'^(?P<agent>KDDI)',     # if no KDDI, HDML browser.
    r'(?P<agent>Vodafone)',

    ....
]


def agent_type(agent_string):
    for d in DETECTOR:
        m = re.search(d, agent_string, flags=re.IGNORECASE)
        m = m and m.groupdict() or {}
        if m:
            return m['agent'].lower().replace('-', '').replace(' ', '')
    return "generic"

Models (accounts / models.py)

from django.db import models
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _

from ua import agent_type
import hashlib

class UserAgent(models.Model):
    agent = models.CharField(
        _(u'User Agent'),
        max_length=30, default="PC")
    key = models.CharField(
        _(u'User Agent Header MD5 Hash'),
        max_length=40, unique=True, db_index=True,)
    header = models.CharField(
        _(u'User Agent Header'),  max_length=512, )
    users = models.ManyToManyField(
        User, default=None, null=True, blank=True)


    @classmethod
    def get_ua(cls, ua_header, user=None):
        ua, created = cls.objects.get_or_create(
            key=hashlib.md5(ua_header).hexdigest())

        if created:
            ua.agent = agent_type(ua_header)
            ua.header = ua_header
            ua.save()

        if user:
            ua.users.add(user)

        return ua

    def __unicode__(self):
        return "%s(%s)" % (self.agent, getattr(self, 'id', ''))

Record in /accounts/profile (acounts/views.py)

--Fixed profile ()

@login_required
def profile(request):
    ua = models.UserAgent.get_ua(
        request.META.get('HTTP_USER_AGENT', 'N/A'),
        request.user)

    return TemplateResponse(
        request,
        'registration/profile.html',
        dict(request=request, ua=ua, ))

My Great

(v17)hdknr@wzy:~/ve/v17/src/ua/web$ python manage.py makemigrations accounts
Migrations for 'accounts':
  0001_initial.py:
    - Create model UserAgent
(v17)hdknr@wzy:~/ve/v17/src/ua/web$ python manage.py migrate  accounts
Operations to perform:
  Apply all migrations: accounts
Running migrations:
  Applying accounts.0001_initial... OK

accounts/admin.py

from django.contrib import admin
import models


class UserAgentAdmin(admin.ModelAdmin):
    list_display = tuple(
        [f.name for f in models.UserAgent._meta.fields
         if f.name not in ['key']]
    )

admin.site.register(UserAgent, UserAgentAdmin)

--At this point, you can manage UserAgent Admin

Admin customization

UserAgentAdmin(accounts/admin.py)

1. Filter by agent field

    list_filter = ('agent', )    

2. Make the users field uneditable

    add_exclude = ('users',)
    edit_exclude = ('users',)

    def add_view(self, *args, **kwargs):
        self.exclude = getattr(self, 'add_exclude', ())
        return super(UserAgentAdmin, self).add_view(*args, **kwargs)

    def change_view(self, *args, **kwargs):
        self.exclude = getattr(self, 'edit_exclude', ())
        return super(UserAgentAdmin, self).change_view(*args, **kwargs)

3. Display the number of users logged in with UserAgent and link to the list

from django.core.urlresolvers import reverse
    list_display = tuple(
        [f.name for f in models.UserAgent._meta.fields
         if f.name not in ['key']]
    ) + ('users_count',)

    def users_count(self, obj):
        if not obj.users.exists():
            return '0'

        uri = reverse("admin:%s_changelist" % obj.users.model._meta.db_table)
        query = "?useragent__id__exact=%d" % (obj.id)
        return mark_safe(
            u"<a href='%s'>%d Users</a>" % (uri + query, obj.users.count()))

    users_count.allow_tags = True

image

4. Instead of not editing the users field, display a link to the logged-in user

    readonly_fields = ('ua_users', )

    def ua_users(self, instance):
        try:
            return ",".join([
                mark_safe('<a href="%s">%s</a>   ' % (
                    reverse("admin:%s_change" % u._meta.db_table, args=[u.id]),
                    u.__unicode__()))
                for u in instance.users.all()])
        except:
            return "errors"

    ua_users.short_description = "UserAgent Users"
    ua_users.allow_tags = True

image

UserAdmin(accounts/admin.py)

--Allows User to return to the User Agent Admin screen

from django.db.models.manager import Manager
from django.utils.safestring import mark_safe
from django.contrib.auth.admin import UserAdmin

--A utility that creates a link to return to the Admin screen for a field when a model and its relation field are specified.

def link_to_relation(self, obj, field=""):
    fobj = obj and getattr(obj, field, None)

    if fobj is None:
        return "No Link"

    if issubclass(fobj.__class__, Manager):
        fobj = fobj.all()
    else:
        fobj = [fobj, ]

    return mark_safe("<br/>".join([
        '<a href="%s">%s</a>' % (
            reverse("admin:%s_change" % ln._meta.db_table, args=[ln.id]),
            ln.__unicode__()
        ) for ln in fobj]))

--Create a backlink with the User object and useragent_set

useragent_link = lambda self, obj: link_to_relation(self, obj, "useragent_set")
useragent_link.short_description = u"User Agent"
useragent_link.allow_tags = True

--Add this to UserAdmin

UserAdmin.list_display = tuple(
    set(UserAdmin.list_display + ('useragent_link', )))
UserAdmin.useragent_link = useragent_link

--In addition, UserAdmin can be filtered by UserAgent # agent

UserAdmin.list_filter = UserAdmin.list_filter + ('useragent__agent',)

――You can go back with this

image

Recommended Posts

Django: Record User Agent and manage with Admin
HTTPS with Django and Let's Encrypt
Manage Django config files with Python-decouple
CentOS 6.4 with Python 2.7.3 with Apache with mod_wsgi and Django
Ramen map creation with Scrapy and Django
Record user last access time with Redis
Manage Django images and static assets on Ubuntu
Generate and post dummy image data with Django
Declaratively manage your environment with Nix and home-manager
Manage state transitions and communicate with smart meters
Internationalization with django
CRUD with Django
Record temperature and humidity with systemd on Raspberry Pi
More new user authentication methods with Django REST Framework
Create APIs around user authentication with Django REST Framework
Implement the Django user extension and register the attached information
Get your current location and user agent in Python
Manage Python runtime packages and development environment packages with Poetry
[Python] Get user information and article information with Qiita API