RentApart and the Death of the Signup Form
A Django rental backend that uses Google OAuth, lazy onboarding, and cookie-based JWTs to turn identity into a staged workflow instead of a one-time gate.
- RentApart turns authentication into a staged state machine, where Google identity, profile completion, and permissions each unlock a different backend state.
- Cookie-based JWTs make token handling safer in the browser, but they also force the app to take CSRF seriously and handle it explicitly.
- The project pushes business rules into Django models and permissions, so the UI reflects backend capability instead of inventing its own logic.
- The codebase feels built for a real market rather than a demo, especially in its Cebu-specific seed data and defensive database constraints.
Most rental backends start with listings and end with auth as an afterthought. RentApart flips that order. Its most interesting idea is that signup is not a form, it is a sequence of backend-verified states.
The Front Door Is Not a Form
RentApart uses Google OAuth as the first identity layer, then checks whether the profile is actually usable. The `Profile` model tracks missing fields like display name, birthday, and phone, and the app only sends the user into onboarding when those gaps exist.
That is a quiet but important design choice. The backend is not asking the frontend to guess what is missing. It is returning a concrete state the UI can react to.
Why Cookie JWTs Change the Threat Model
Most DRF projects put JWTs in headers. RentApart stores them in HttpOnly cookies instead. That narrows the XSS theft problem, but it makes CSRF unavoidable, so the authentication layer explicitly enforces CSRF checks.
| Header JWTs | Cookie JWTs |
|---|---|
| Tokens travel in request headers and are easy to attach from the client. | Tokens sit in HttpOnly cookies and are harder for script to steal. |
| XSS token theft is the main browser risk. | CSRF becomes the trade-off that must be handled explicitly. |
| Client code decides when to send the token. | The browser sends it automatically with the request. |
| Simple mental model, weaker browser isolation. | Stronger token storage, stricter request validation. |
The Backend Decides What the UI Can Do
RentApart exposes capability data from the backend, including leasing-related permissions. That means the frontend is not inferring what a user can do from scattered hints. It receives a backend-authored answer and renders accordingly.
This is more than access control. It is interface control. The same permission system that blocks writes also shapes which screens and actions should even appear.
Listings Are the Real Product Surface
The listing engine is where the product becomes tangible. Search spans title, description, city, and neighborhood. Landlord assignment is constrained with `limit_choices_to`, so only users with the right permission can be attached as managers.
That constraint matters because it prevents the data model from drifting away from the product rules. A bad UI cannot quietly assign the wrong landlord if the model itself refuses the state.
class Listing(models.Model):
landlord = models.ForeignKey(
Profile,
on_delete=models.CASCADE,
limit_choices_to=_lease_manager_profiles_q,
)
class ListingFilter(filters.FilterSet):
def filter_q(self, queryset, name, value):
return queryset.filter(
Q(title__icontains=value)
| Q(description__icontains=value)
| Q(city__icontains=value)
| Q(neighborhood__icontains=value)
)
Defensive Django in the Database Layer
RentApart uses constraints where a less careful project would rely on validation alone. Booking end times must come after start times. Duplicate rental applications are blocked at the database level. These are the kinds of rules that keep working when the client misbehaves.
| Rule | Where it lives | Why it matters |
|---|---|---|
| Booking end after start | Database CheckConstraint | Invalid time ranges cannot slip through a buggy client. |
| One application per listing per renter | unique_together | Duplicate submissions are rejected even if the UI retries. |
| Only lease managers can be landlords | Model-level choice restriction | Permission errors are prevented before they become bad data. |
Seeding a City, Not Just a Database
The seed command is one of the strongest signals in the repository. It does not generate generic filler. It uses neighborhood-specific data and realistic property imagery tied to Cebu City, which makes the system feel like it was built for a market, not for a demo.
That choice changes how the whole project reads. The backend is not just structurally sound. It is anchored to a place with enough realism to suggest a real product path.
What Is Finished, and What Is Still a Skeleton
The codebase is coherent, but not finished. The listings work feels the most mature. The applications app looks thinner. The documentation is minimal. Even so, the direction is clear: a secure, opinionated rental backend where identity and permissions are core product surfaces.
That is what makes RentApart worth noticing. It is not trying to be a generic clone. It is using Django to turn onboarding, security, and model constraints into part of the product itself.