Skip to main content

A tiny flexible DRF toolkit for user registration from views.py in a few lines.

Project description

Humanoid User Registration

Humanoid User Registration is a tiny toolkit on top of Django REST Framework that helps you create a flexible user registration API from views.py in only a few lines.

It is designed for developers who do not want to write a new serializer every time they need user registration.

from humanoid_user_registration import registration_view, field, types

register = registration_view(
    fields=[
        field.username,
        field.email(types.email, required=True),
        field.given_name(types.string, source="first_name"),
        field.family_name(types.string, source="last_name"),
        field.phone_number(types.string, required=True),
    ],
    allow_non_model_fields=True,
)

Main idea

You configure registration directly inside your Django views.py.

You do not need to add package settings like this:

HUMANOID_USER_REGISTRATION = {...}

You do not need to create a custom registration serializer manually.

You do not need to add this package to INSTALLED_APPS unless you want to use optional features from your own project.

You only import the toolkit and create a DRF view.


Installation

pip install humanoid-user-registration

Development install from local source:

pip install -e .

Requirements

  • Python 3.9+
  • Django 4.2+
  • Django REST Framework 3.14+

Quick start

1. Create your registration view in views.py

from humanoid_user_registration import registration_view, field, types

register = registration_view(
    fields=[
        field.username,
        field.email(types.email, required=True),
        field.first_name(required=False),
        field.last_name(required=False),
    ]
)

2. Connect your URL

Your Django project still needs a URL route. This is a Django rule: a request cannot reach a view unless some URL points to it.

Example in urls.py:

from django.urls import path
from .views import register

urlpatterns = [
    path("api/auth/register/", register, name="register"),
]

If your project already routes to this views.py, then you only work in views.py.

3. Send a POST request

{
  "username": "hekmat",
  "email": "hekmat@example.com",
  "first_name": "Hekmat",
  "last_name": "Rahimi",
  "password": "StrongPass123!",
  "password_confirm": "StrongPass123!"
}

Example success response:

{
  "message": "Registration successful.",
  "user": {
    "id": 1,
    "username": "hekmat",
    "email": "hekmat@example.com",
    "first_name": "Hekmat",
    "last_name": "Rahimi"
  }
}

Important: JSON keys need quotation marks

In JSON, keys must use quotation marks:

{
  "username": "hekmat"
}

This is invalid JSON:

{
  username: "hekmat"
}

But inside Python views.py, the toolkit lets you avoid raw string dictionaries by using this clean style:

field.age(types.integer)
field.phone_number(types.string)
field.given_name(types.string, source="first_name")

Field syntax

Simple model fields

from humanoid_user_registration import registration_view, field, types

register = registration_view(
    fields=[
        field.username,
        field.email(types.email),
        field.first_name,
        field.last_name,
    ]
)

Required and optional fields

register = registration_view(
    fields=[
        field.username,
        field.email(types.email, required=True),
        field.first_name(required=False),
        field.last_name(required=False),
    ]
)

More field types

register = registration_view(
    fields=[
        field.username,
        field.email(types.email),
        field.age(types.integer, required=False),
        field.is_student(types.boolean, required=False),
        field.date_of_birth(types.date, required=False),
        field.website(types.url, required=False),
    ],
    allow_non_model_fields=True,
)

Supported field types

Use these inside views.py:

types.string
types.email
types.integer
types.boolean
types.date
types.datetime
types.float
types.decimal
types.url
types.uuid
types.text

There is also a type alias:

from humanoid_user_registration import registration_view, field, type

register = registration_view(
    fields=[
        field.username,
        field.age(type.integer),
    ]
)

But types.integer is recommended because type is already a Python built-in name.


Rename API fields with source

Sometimes your frontend field name is different from your Django User model field name.

Example: frontend sends given_name, but Django User model has first_name.

register = registration_view(
    fields=[
        field.username,
        field.email(types.email),
        field.given_name(types.string, source="first_name"),
        field.family_name(types.string, source="last_name"),
    ]
)

Frontend request:

{
  "username": "hekmat",
  "email": "hekmat@example.com",
  "given_name": "Hekmat",
  "family_name": "Rahimi",
  "password": "StrongPass123!",
  "password_confirm": "StrongPass123!"
}

Saved in Django:

given_name  -> User.first_name
family_name -> User.last_name

Add non-model fields

A model field is a real database/User model field. For Django's default User model, common fields are:

username
email
first_name
last_name
password

A non-model field is a field that does not exist in your User model, for example:

phone_number
address
nid_number
age

To accept non-model fields:

register = registration_view(
    fields=[
        field.username,
        field.email(types.email),
        field.phone_number(types.string, required=True),
        field.address(types.string, required=False),
        field.age(types.integer, required=False),
    ],
    allow_non_model_fields=True,
)

Request:

{
  "username": "hekmat",
  "email": "hekmat@example.com",
  "phone_number": "0700000000",
  "address": "Kabul",
  "age": 22,
  "password": "StrongPass123!",
  "password_confirm": "StrongPass123!"
}

What happens:

username      saved if User.username exists
email         saved if User.email exists
phone_number  accepted and returned, but not permanently saved unless User.phone_number exists
address       accepted and returned, but not permanently saved unless User.address exists
age           accepted and returned, but not permanently saved unless User.age exists

Important: allow_non_model_fields=True does not create database columns. It only allows the API to accept extra fields without crashing.

If you want non-model fields to be saved permanently, use one of these professional options:

  1. Add the fields to your custom User model.
  2. Create a separate Profile model.
  3. Save the extra values in a post_register_hook.

Use post_register_hook

You can run custom logic after the user is created.

from humanoid_user_registration import registration_view, field, types


def after_register(user, request):
    phone_number = request.data.get("phone_number")
    print("New user:", user.username)
    print("Phone:", phone_number)


register = registration_view(
    fields=[
        field.username,
        field.email(types.email),
        field.phone_number(types.string, required=True),
    ],
    allow_non_model_fields=True,
    post_register_hook=after_register,
)

You can also define a hook with three parameters:

def after_register(user, request, extra_fields):
    print(extra_fields)

The toolkit will pass non-model fields to extra_fields.


Custom response fields

By default, the response includes id and the configured fields.

You can control the response:

register = registration_view(
    fields=[
        field.username,
        field.email(types.email),
        field.given_name(types.string, source="first_name"),
    ],
    response_fields=["id", "username", "email", "given_name"],
)

Disable password confirmation

Default request requires both:

{
  "password": "StrongPass123!",
  "password_confirm": "StrongPass123!"
}

To use only password:

register = registration_view(
    fields=[field.username, field.email(types.email)],
    require_password_confirmation=False,
)

Password validation

By default, the toolkit calls Django's password validators.

To disable password strength validation:

register = registration_view(
    fields=[field.username, field.email(types.email)],
    validate_password_strength=False,
)

To add a minimum length at serializer level:

register = registration_view(
    fields=[field.username, field.email(types.email)],
    password_min_length=8,
)

Include DRF token in response

If your project uses DRF TokenAuthentication and has rest_framework.authtoken installed, you can return a token after registration.

register = registration_view(
    fields=[field.username, field.email(types.email)],
    include_token=True,
)

Response:

{
  "message": "Registration successful.",
  "user": {
    "id": 1,
    "username": "hekmat",
    "email": "hekmat@example.com"
  },
  "token": "abc123..."
}

If rest_framework.authtoken is not installed, the user is still created, but no token is returned.


Custom permissions, authentication, and throttling

from rest_framework.throttling import AnonRateThrottle

register = registration_view(
    fields=[field.username, field.email(types.email)],
    throttle_classes=[AnonRateThrottle],
)

By default, registration uses AllowAny permission.


Full realistic example

# views.py
from humanoid_user_registration import registration_view, field, types


def after_register(user, request, extra_fields):
    # Example: save phone_number to a Profile table in your own project.
    # Profile.objects.create(user=user, phone_number=extra_fields.get("phone_number"))
    pass


register = registration_view(
    fields=[
        field.username,
        field.email(types.email, required=True),
        field.given_name(types.string, source="first_name", required=True),
        field.family_name(types.string, source="last_name", required=False),
        field.phone_number(types.string, required=True),
        field.age(types.integer, required=False),
        field.date_of_birth(types.date, required=False),
    ],
    allow_non_model_fields=True,
    response_fields=[
        "id",
        "username",
        "email",
        "given_name",
        "family_name",
        "phone_number",
        "age",
        "date_of_birth",
    ],
    post_register_hook=after_register,
)

Request:

{
  "username": "hekmat",
  "email": "hekmat@example.com",
  "given_name": "Hekmat",
  "family_name": "Rahimi",
  "phone_number": "0700000000",
  "age": 22,
  "date_of_birth": "2004-01-01",
  "password": "StrongPass123!",
  "password_confirm": "StrongPass123!"
}

Older supported styles

The recommended style is:

field.age(types.integer, required=False)

But these also work:

"username"
{"name": "age", "type": "integer", "required": False}

This keeps the toolkit flexible.


Optional built-in URL

The package includes an optional default URL:

from django.urls import include, path

urlpatterns = [
    path("api/auth/", include("humanoid_user_registration.urls")),
]

This creates:

POST /api/auth/register/

But the main goal of this package is still the views.py style.


Development

Clone the project:

git clone https://github.com/your-username/humanoid-user-registration.git
cd humanoid-user-registration

Install dev dependencies:

python -m pip install -e ".[dev]"

Run tests:

pytest

Run linting:

ruff check .

Build package:

python -m build

Check package:

python -m twine check dist/*

Upload to TestPyPI:

python -m twine upload --repository testpypi dist/*

Upload to PyPI:

python -m twine upload dist/*

GitHub Actions publishing

This package includes .github/workflows/publish.yml for PyPI Trusted Publishing.

Recommended flow:

  1. Push your code to GitHub.
  2. Create the project on PyPI.
  3. Configure Trusted Publisher on PyPI for your GitHub repository.
  4. Create a GitHub release.
  5. GitHub Actions builds and publishes the package.

Security notes

  • This toolkit uses Django's active User model through get_user_model().
  • Passwords are saved through Django's create_user() method.
  • Django password validators are enabled by default.
  • Non-model fields are not saved unless you save them yourself through a hook or your own model.
  • Always use HTTPS in production.
  • Add throttling/rate limiting to public registration endpoints.

License

MIT License.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

humanoid_user_registration-1.0.0.tar.gz (17.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

humanoid_user_registration-1.0.0-py3-none-any.whl (14.8 kB view details)

Uploaded Python 3

File details

Details for the file humanoid_user_registration-1.0.0.tar.gz.

File metadata

File hashes

Hashes for humanoid_user_registration-1.0.0.tar.gz
Algorithm Hash digest
SHA256 68e554a620b6ac1890ef035f8d7e820a0f18f5b9d8ab874d232bf00d415e0685
MD5 4cc7d1309228479e3b44d168e5b7678d
BLAKE2b-256 8e0406ce46114740171878b81d2cac0d5a2d71cb73d9809d94b7fbd6520baf41

See more details on using hashes here.

File details

Details for the file humanoid_user_registration-1.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for humanoid_user_registration-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 41a572188cb0a046fa476b01027fa0acfed66c6be97500db1e63208b959abcd3
MD5 bed9f6c1dcc663a439ece4055885c350
BLAKE2b-256 a385bf7c72f31515c07510066ed9754a2c36ab1e8d3bc78909c6ac1a481bbfd4

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page