Skip to content

Conversation

@vic012
Copy link

@vic012 vic012 commented Jul 29, 2024

Description:

This PR adds a new helper function to the localflavor.br module, encapsulated in a utils.py file. The function, get_state_name_from_state_abbreviation, allows developers to convert a Brazilian state abbreviation (UF) into its full state name with a simple, consistent API.

Key Changes:

Introduces STATE_CHOICES_DICT, a constant dictionary mapping all Brazilian UF abbreviations to their full names (derived from STATE_CHOICES).

  • Adds get_state_name_from_state_abbreviation:

  • Accepts a string abbrev (e.g., "RJ" or "sp").

  • Normalizes input to upper case.

  • Returns the full state name (e.g., "Rio de Janeiro"), or None if the abbreviation is invalid or not a string.

  • Includes unit tests covering:

  • Valid abbreviations (both upper and lower case),

  • Invalid abbreviations,

  • Non-string inputs.

  • Updates docs/changelog.rst with a description of the new functionality.

  • Adds the contributor’s name to docs/authors.rst.

Copy link

@leogregianin leogregianin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Useful for many applications

@benkonrath
Copy link
Member

@vic012 Can you provide some information about how this fits into django-localflavor? Reading through get_states_of_brazil(), it seems to be a helper function / application specific logic, rather than something connected to a Django model or form. I don't use the Brazil locolflavor personally, so I don't know how it's useful to developers in Brazil who use the BR flavor. Thanks.

@vic012
Copy link
Author

vic012 commented Jul 17, 2025

Thanks for the feedback, @benkonrath !

You're right — get_states_of_brazil() is a helper function and not directly tied to models or forms. However, its purpose is to centralize and simplify access to the list of Brazilian states already defined in STATE_CHOICES.

Why it fits into django-localflavor:

  • Avoids repetition: Many Brazilian developers need to access the state name based on the UF (e.g., "SP""São Paulo"), especially when integrating form input with external APIs or when customizing templates or business logic.
  • Based on existing localflavor data: It reuses STATE_CHOICES, so it stays consistent with the values already defined by the package.
  • Encourages best practices: Rather than copying the state list or writing their own mapping, developers can use a tested, documented utility.
  • Supports common Brazilian workflows: In many Brazilian applications, returning a full list of state names or formatting them (capitalized or not) is a very common task.

Example use case:

Let’s say a user stores "RJ" in a model field and wants to display "Rio de Janeiro" in a template or a PDF report:

from localflavor.br.utils import get_states_of_brazil

print(get_states_of_brazil("RJ")) # Rio de Janeiro
# or
print(get_states_of_brazil("rj"))  # Rio de Janeiro

print(get_states_of_brazil("RJ", capital_letter=True)) # RIO DE JANEIRO
# or
print(get_states_of_brazil("rj", capital_letter=True))  # RIO DE JANEIRO

print(get_states_of_brazil("Rt", capital_letter=True)) # {'AC': 'ACRE',  'AL': 'ALAGOAS',  'AP': 'AMAPÁ', ...}
# or
print(get_states_of_brazil("Rt")) # {'AC': 'Acre', 'AL': 'Alagoas', 'AP': 'Amapá', ...}

print(get_states_of_brazil(capital_letter=True)) # {'AC': 'ACRE',  'AL': 'ALAGOAS',  'AP': 'AMAPÁ', ...}
# or
print(get_states_of_brazil()) # {'AC': 'Acre', 'AL': 'Alagoas', 'AP': 'Amapá', ...}
# models.py
from django.db import models
from localflavor.br.br_states import STATE_CHOICES

class UserProfile(models.Model):
    name = models.CharField(max_length=100)
    state = models.CharField(max_length=2, choices=STATE_CHOICES)

# views.py
from django.shortcuts import render
from localflavor.br.utils import get_states_of_brazil
from .models import UserProfile

def profile_view(request, user_id):
    profile = UserProfile.objects.get(id=user_id)
    state_name = get_states_of_brazil(profile.state)
    return render(request, 'profile.html', {'profile': profile, 'state_name': state_name})
{# profile.html #}
<p>User: {{ profile.name }}</p>
<p>State: {{ state_name }}</p>

Using raw STATE_CHOICES directly in a large codebase:

In large projects with multiple developers, it's common to see repetitive logic written in different parts of the codebase to convert Brazilian state abbreviations into full names using the STATE_CHOICES variable.

Here's an example of how it's typically done without a helper:

from localflavor.br.br_states import STATE_CHOICES

def get_state(federative_unit=None, capital_letter=False):
    state_choices_available = {
        acronym.upper(): name.upper() if capital_letter else name
        for acronym, name in STATE_CHOICES
    }

    if federative_unit is None:
        return state_choices_available

    federative_unit = str(federative_unit).upper()

    return state_choices_available.get(federative_unit, state_choices_available)
    
# or

def get_state_name(federative_unit=None, capital_letter=False):
    state_dict = {
        acronym.upper(): name.upper() if capital_letter else name
        for acronym, name in STATE_CHOICES
    }
    return state_dict.get(str(federative_unit).upper(), state_dict)

Although this works, it tends to be copied and adapted differently across the project — sometimes with minor variations in casing, error handling, or naming conventions. This increases the risk of inconsistencies and makes the code harder to read, debug, and maintain over time.

By centralizing this logic in a utility function like get_states_of_brazil, we:

  • Promote code reuse and avoid duplication.
  • Keep logic consistent and predictable across the team.
  • Make it easier to maintain and improve in a single place.
  • Reduce the chance of bugs caused by inconsistent implementations.

In short, a shared utility improves readability, maintainability, and developer experience in growing codebases.

@vic012
Copy link
Author

vic012 commented Jul 17, 2025

I understand that this is a basic utility and not everyone may find it necessary. However, I've personally needed a similar function in the past, and I believe this addition could be helpful to others as well. Just as localflavor has supported many applications around the world with simple but essential tools, this small contribution might prove useful in similar scenarios.

@rennerocha
Copy link

I am not sure if this function is really useful for a broader user base or something that is useful just for a few use cases. I also see some problems with this function that could be improved anyway:

  1. The name is not ideal. get_states_of_brasil (states in plural) leads me to believe that I will receive a list (or at least a dictionary) with the states of Brazil. However calling get_states_of_brazil("RJ") returns me the string Rio de Janeiro.
  2. But if I call it without arguments (get_states_of_brazil()), the response is a dictionary generated with the contents of the existing STATE_CHOICES list of tuples. This is not a good design as the same function returns completely different types of information.
  3. String processing (upper vs lower case) can be done outside the function (using Python stdlib or if the information is supposed to be presented in a Django template using upper template tag
  4. A better name could be get_state_name_from_state_abbreviation which presents exactly the intent of it (given a state abbreviation string, return the full state name), and the implementation is very simple with some processing of STATE_CHOICES to convert it to a dictionary once (this could be defined as a constant and reused later)

@vic012
Copy link
Author

vic012 commented Nov 19, 2025

Thank you for the detailed feedback @rennerocha !

I agreed with your points regarding naming, expected return types, and keeping the function focused on a single use case. Based on your suggestions, I’ve updated the PR with the following changes:

  • Renamed the function to get_state_name_from_state_abbreviation, which better reflects its purpose;
  • Simplified the behavior so it now accepts only a single abbreviation and always returns either a full state name or None;
  • Created a STATE_CHOICES_DICT constant to avoid reconstructing the mapping on every call;
  • Removed parameters and functionality that produced inconsistent return types;
  • Added unit tests to validate common and edge cases.

I believe this version is clearer, more predictable.
Thanks again for guiding the improvement — I really appreciate it!

@vic012 vic012 changed the title Added a utils.py get_states_of_brazil Added a utils.py get_state_name_from_state_abbreviation Nov 19, 2025
Added new utility functions for Brazilian state names and options.
Removed duplicate entry for CIN Number field in Morocco flavor and consolidated related information.
@vic012 vic012 requested a review from leogregianin November 26, 2025 15:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants