Django MongoDB Model field with unique values or null - django

I am using Djongo v1.3.6 to connect Django to MongoDB. Now I would like to have an optional field for a unique value - in my case a phone number. I thought it is possible to have null as a placeholder for accounts that do not have a phone number. However, MongoDB seems to be treating null as a unique value as well. Thus, it is not letting me insert new objects into the database once one object has phone_number: null
I tried to declare the index for phone_number as sparse but it does not seem to take any effect. I searched the Internet for some time now but could not find anything useful for my case.
class UserProfile(models.Model):
user = models.OneToOneField(AUTH_USER_MODEL, on_delete=models.CASCADE)
phone_number = models.CharField(validators=[PHONE_VALIDATOR], max_length=17, unique=True, null=True, blank=True)
...
meta = {
'indexes': [
{'fields': ['phone_number'], 'sparse' : True, 'unique' : True},
],
}
Any help is very appreciated.

I solved this issue altering the index that is created by Djongo using pymongo.
from pymongo import MongoClient, database, collection
collection.drop_index(index_name)
index_name = rchop(index_name, '_1')
collection.create_index(
[(index_name, pymongo.ASCENDING)],
partialFilterExpression = {
index_name : { "$exists" : True, "$gt" : "0", "$type" : "string" }
}
)
Once I had altered the index, I was able to insert null values into MongoDB without sacrificing the unique check for non-null values

Related

Django django.db.utils.IntegrityError

Model:
class List(models.Model):
Lid = models.AutoField(primary_key=True)
Name = models.CharField(max_length=100)
addr1 = models.CharField(max_length=100)
addr2 = models.CharField(max_length=100)
City = models.CharField(max_length=40)
State = models.ForeignKey(State,blank=True, on_delete=models.DO_NOTHING, default=None,to_field="state",db_column="State") #,to_field="state",db_column="State"
Below is the error appears when tried to migrate,
IntegrityError(
django.db.utils.IntegrityError: The row in table 'list' with primary key '1' has an invalid foreign key: list.State contains a value '' that does not have a corresponding value in State.state.
How to fix this issue? I did add those 'blank=True' and on_delete=models.DO_NOTHING after searching for a solution in google, still no luck.
If you want have row from List and 1 row from State.
It can be o2o-key (don't matter which model), or you can use Foreign-key in List.
But: DB-Table State should have in your db_column="State" only unique keys.
If you want to have every row from List and some rows from State.
Foreign key should be in State model, not in List.
After that: On migration you should convert column list.state to value_type like in state.state.
For example you have type(list.state) = string and type(State.state) = integer. It can works without it, i know, but it is better to check it on migration.
if you have in list.state default=None, also you can convert every_list_row.state = '' to every_list_row.state = None, to avoid problem in future, on export etc.
If you receive ForeignObjectException - object not exists on list_row.state:
You can create something like that:
#property
def get_state(self):
return hasattr(list_row, 'state') and list_row.state or ''
and after that: list_row.get_state

Django Admin - Show form json input value as text insted string

Considering I have a model like:
MyStore = (
id = 1,
name = 'Foobar',
information_as_json = {
'open_at': datetime.now(),
'close_at': datetime.now() + timedelta('+1 day'),
'workers' : {
'Person1' : 'Owner',
'Person2' : 'Boss',
'Person3' : 'Boss',
}
})
Inside Django admin forms, for every field is generated an input, but for the field "information_as_json", I don't want to show it as a string or as JSON. That is because the users who are accessing this store admin page, need to read the field 'information_as_json' easier since no one can edit these values because it is generated in another part of the application.
Is it possible to convert these values to a "div" or a plain text? The contents would be:
This store opens at: {information_as_json.open_at}
This store close at: {information_as_json.close_at}
And for the workers, iterate through keys and values:
for key, value in information_as_json.workers:
Worker {key} has the role: {value}
I'm a beginner at Django, so I'm struggling a little with this part.
Every help would be appreciated :D
I would suggest approaching the model a little differently. Rather than storing the opening and closing hours as JSON they can just be fields directly on the store model. The the workers can be a JSONfield [docs] containing name/role pairs. If you're using PostgreSQL for your database you could even use HStoreField [docs], which might be more appropriate.
Here's how I would write a similar model.
class Store(models.Model):
name = models.CharField(max_length=512, unique=True)
workers = models.JSONField(blank=True, default=dict, editable=False)
closing = models.TimeField(blank=True, null=True, editable=False)
opening = models.TimeField(blank=True, null=True, editable=False)
To display the details in the Django admin we just need to define a property which returns the correct string.
#mark_safe
def details(self):
roles = [
f'{x} has the role: {y}'
for x, y in self.workers.items()
]
return '<br>'.join([
f'This store opens at: {self.opening:%-H:%M}',
f'This store closes at: {self.closing:%-H:%M}',
] + roles)
This method can then be referenced in the ModelAdmin and used like a read-only field.
#admin.register(Store)
class StoreAdmin(admin.ModelAdmin):
list_display = ['name', 'opening', 'closing']
fields = ['name', 'details']
readonly_fields = ['details']

Django fixtures and auto_now_add date time field

Trying to create a fixture for a model with an auto_now_add date time field
created_at = models.DateTimeField(auto_now_add=True)
When the fixtures is loaded there is an error thrown IntegrityError: Problem installing fixture, null value in column "created_at" violates not-null constraint
Is there a way to have Django determine the date rather than manually entering a date?
[
{
"model": "customer.type",
"pk": 1,
"fields": {
"name": "type",
"created_by": 1
}
}
]
One of the easy workaround will be using the default--[DjangoDoc] and editable--[DjangoDoc] arguments together,
from django.utils import timezone
class Foo(models.Model):
...
created_at = models.DateTimeField(default=timezone.now, editable=False)
The above solution tested and verified under Django 2.1 and Python 3.6 environment.
Drawback of this method
From the Django-Doc of DateField.auto_now_add
Automatically set the field to now when the object is first created. Useful for creation of timestamps. Note that the current date is always used; it’s not just a default value that you can override. So even if you set a value for this field when creating the object, it will be ignored. If you want to be able to modify this field, set the following instead of auto_now_add=True
Which means, this setting will override the timezone.now() value if you manually provide any valid datetime.

Django: null value in column "created_at" violates not-null constraint

I'm trying to add records via the code below:
Post.objects.update_or_create(
user=user,
defaults={
"title": external_post.get('title'),
"body": external_post.get('body'),
"seo": external_post.get('seo')
}
)
I've successfully migrated the model but I'm getting the error " null value in column "created_at" violates not-null constraint".
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
I faced this same problem when I was using the #transaction atomic decorator in Django. Basically the reason why I faced the same error was that I was not using the default auto-increment ID in one of my models but rather I had specified a particular field as the primary key using primary_key=True
As a result my data contained two primary keys that were the same. This resulted in an 'update' operation rather than a 'create' operation in the database.
So, Django was trying to update an entry but the created_at field was missing hence the error.
I would suggest you do this instead:
post,created = Post.objects.update_or_create(
user=user,
defaults={
"title": external_post.get('title'),
"body": external_post.get('body'),
"seo": external_post.get('seo')
})
if created:
# your code goes here
#(this ensures that the code is executed only if an entry is getting created in the database)
You can read this for a better explaination: https://code.djangoproject.com/ticket/17654

how to update unique field for multiple entries in django

I have a simple Django model similar to this:
class TestModel(models.Model):
test_field = LowerCaseCharField(max_length=20, null=False,
verbose_name='Test Field')
other_test_field = LowerCaseCharField(max_length=20, null=False, unique=True,
verbose_name='Other Test Field')
Notice that other_test_field is a unique field. Now I also have some data stored that looks like this:
[
{
test_field: "object1",
other_test_field: "test1"
},
{
test_field: "object2",
other_test_field: "test2"
}
]
All I'm trying to do now is switch the other_test_field fields in these two objects, so that the first object has "test2" and the second object has "test1" for other_test_field. How do I accomplish that while preserving the uniqueness? Ultimately I'm trying to update data in bulk, not just swapping two fields.
Anything that updates data in serial is going to hit an IntegrityError due to unique constraint violation, and I don't know a good way to remove the unique constraint temporarily, for this one operation, before adding it back. Any suggestions?