django Charfield suitable for a primary key?

There is nothing wrong with setting the CharField to be a primary key, by changing the model to be:

class PaymentMethod(models.Model):
    unique_id = models.CharField(max_length=3, primary_key=True)
    last_updated = models.DateTimeField(auto_now=True)

In actual fact if the unique_id is the field you will be querying it makes perfect sense to use it. Your other options are to use your existing model but with unique=True:

class PaymentMethod(models.Model):
    unique_id = models.CharField(max_length=3, unique=True)
    last_updated = models.DateTimeField(auto_now=True)

In this case your primary key will be an auto incrementing integer as you previously stated.

Another option depending on the number of records you are looking to store in the PaymentMethod models; and where the data is used elsewhere within your application. Is too look into using a model choices field. The model choices field might be on your payments or order model (depends on your application and what you are trying to do). This removes the need for a foreignkey and potentially reduces the number of queries in your app.

It could look something like this:

class Payment(models.Model):
    VISA = 'VIS'
    CREDIT = 'CRE'
    MASTER_CARD = 'MAS'
    PAYPAL = 'PAL'
    PAYMENT_OPTIONS= (
        (VISA, 'Visa'),
        (CREDIT, 'Credit Card'),
        (MASTER_CARD, 'Master Card'),
        (PAYPAL, 'Paypal')
    )

    items = models.ForeignKey(Item)
    date = models.DateField(auto_now=True)
    ...
    payment_method = models.CharField(max_length=3, choices=PAYMENT_OPTIONS, default=VISA)

The PAYMENT_OPTIONS can be used to render dropdown boxes on the forms when using django model forms. Otherwise the users selection is limited to the options listed within this model. This method would be a lot more efficient if you only have a small subset of PaymentMethod(s).


It's possible to use CharField as primary key. You just have to mark the field as primary key.

field_name = models.CharField(primary_key=True, max_length=100)

But I wouldn't recommend it because:

  • Primary keys are used in urls (typically in Rest APIs) - but not all characters are allowed in urls
  • DRF (django-rest-framework) use urls patterns that don't catch some characters by default (for example ".")
  • Primary keys must be unique - it's harder to accomplish it if the field is a string, especially when you let users to define it