Generating a Django Model that can have multiple values in one field - django

I am trying to generate a Django model that can handle multiple values in a single field. As such, when the first field is queried through a view, a user should select a value for the second field through a select box.
To give a background of the problem, my seeding fixture looks like this...
[
{
"model":"myapp.location",
"pk":1,
"fields":{
"county": "countyname",
"places":{
"name": "placename",
"name": "placename",
"name": "placename",
"name": "placename",
"name": "placename"
}
}
}
]
In the above scenario, location is the intended name of my model. Now, through a form, I want a user to be presented with 'countynames'. Upon selecting a countyname, the user should be presented with 'placenames' in the selected county for them to choose.
I have tried the following format for the model...
class Location(models.Model):
county = models.CharField(max_length=100)
places = models.CharField(max_length=100, choices=places.name)
def __str__(self):
return self.countyname
Now, I know that the error that is thrown, ('places' is not defined), is warranted. I was asking whether there is a way to define it (places), as it is in the fixture, or if anyone has a better implementation for such a model... any alternative way is welcome and appreciated as I can't think of anything at this point.

So, after fiddling with two models and foreign keys as suggested in the comments above, I decided to amend the model, which also led to changing the fixture. I read about ArrayFields in Postgres + Django here. I amended the field 'places' to be an ArrayField as shown:
from django.contrib.postgres.fields import ArrayField
class Location(models.Model):
county = models.CharField(max_length=100)
places = ArrayField(models.CharField(max_length=100), blank=True)
def __str__(self):
return self.county
Next, it was just a matter of changing the JSON fixture to:
[
{
"model":"myapp.location",
"pk":1,
"fields":{
"county": "countyname",
"places":["placename","placename","placename","placename"]
}
}
]
After running python manage.py loaddata fixturename.json ,
it worked and the DB was seeded!

Related

wagtail search_fields on snippet with foreign key

I have a snippet which is a proxy of one of my standard django models.
search_fields works fine when filtering on standard fields, the problem is I can't seem to get foreign keys to work.
This page has an example on the bottom that shows how to create searchable snippets:
https://docs.wagtail.org/en/stable/topics/snippets.html
The main model has a field called "day" which is a foreign key to a Day-table. A day has a calendar_year, which I would like to be able to filter on while searching in the wagtail snippets area. in the def str method I'm able to display the name in the list, the search is the problem here.
Suggestions?
#register_snippet
class EventSnippet(index.Indexed, Event):
# We make a proxy model just to be able to add to this file or potentially if we want custom methods on it.
panels = [
FieldPanel('name'),
]
search_fields = [
index.SearchField('day__calendar_year', partial_match=True), # This prompts an error
index.SearchField('name', partial_match=True),
]
class Meta:
proxy = True
def __str__(self):
return f"{self.name} {self.day.calendar_year}"
When running python manage.py update_index i get the following warning:
EventSnippet.search_fields contains non-existent field 'day__calendar_year
You can't use complex lookups with double-underscores inside SearchField - search queries work by populating a central table (the search index) in advance with the data you're going to be searching on, which means you can't do arbitrary lookups and transformations on it like you would with a standard database query.
However, you can use any method or attribute in SearchField - not just database fields - so you could add a method that returns the year, and use that:
#register_snippet
class EventSnippet(index.Indexed, Event):
# ...
def get_year(self):
return self.day.calendar_year
search_fields = [
index.SearchField('get_year', partial_match=True),
index.SearchField('name', partial_match=True),
]

How to ignore special characters from the search field in Django

The model is something like
class Product(BaseModel):
name = models.CharField(db_column='name', max_length=200, blank=False, null=False, unique=True)
View is
class ProductViewSet(BaseViewSet):
queryset = Product.objects.all()
...
filterset_class = ProductFilter
The filter is
class ProductFilter(django_filters.FilterSet):
search = django_filters.CharFilter(field_name='name', lookup_expr='icontains')
class Meta:
model = Product
fields = []
Now.. if the name field has a value something like "This is a/sample" and search text is "asample". I would like to return that row.
Thanks in advance.
If the question is only for one special character ie. '/' then you can create a custom filter method with Replace like this :
class ProductFilter(django_filters.FilterSet):
def filter_without_special_chars(self, queryset, field, value):
return queryset.annotate(search_field=Replace('name', Value('/'), Value('')).filter(search_field__icontains=value)
search = django_filters.CharFilter(method='filter_without_special_chars')
class Meta:
model = Product
fields = []
You can also do this for multiple special characters BUT it won't be the optimal solution, I would suggest you user ElasticSearch (or something similar) for that.
For multiple char replacement the function would look something like this :
def filter_without_special_chars(self, queryset, field, value):
return queryset.annotate(sf1=Replace('name', Value('!'), Value('')),
sf2=Replace('sf1', Value('%'), Value(''))).filter(sf2__icontains=value)
Use PostGreSQL, which currently supports the 'unaccent' extension. This makes searching for 'año' possible when only typing 'ano'.
Best thing is, you can decide whether to use this extension for every filter by, for example using
Person.objects.filter(first_name__unaccent__icontains=search)
Switch your database to PostgreSQL and add the unaccent extension as follows:
Part of answer from #SaeX in another thread:
How can I activate the unaccent extension on an already existing model
A migration file needs to be manually made and applied.
First, create an empty migration:
./manage.py makemigrations myapp --empty
Then open the file and add UnaccentExtension to operations:
from django.contrib.postgres.operations import UnaccentExtension
class Migration(migrations.Migration):
dependencies = [
(<snip>)
]
operations = [
UnaccentExtension()
]
Now apply the migration using ./manage.py migrate.
If you'd get following error during that last step:
django.db.utils.ProgrammingError: permission denied to create extension "unaccent"
HINT: Must be superuser to create this extension.
... then temporarily allow superuser rights to your user by performing postgres# ALTER ROLE <user_name> SUPERUSER; and its NOSUPERUSER counterpart. pgAdminIII can do this, too.
Now enjoy the unaccent functionality using Django:
>>> Person.objects.filter(first_name__unaccent=u"Helène")
[<Person: Michels Hélène>]
Again, part of this answer belongs to #SaeX
IMPORTANT
But for me his answer still didn't work, so don't forget to
add the line django.contrib.postgresin INSTALLED_APPS (settings.py)

Django, Create GIN index for child element in JSON Array field

I have a model that uses PostgreSQL and has field like this:
class MyModel(models.Model):
json_field = models.JSONField(default=list)
This field contains data like this:
[
{"name": "AAAAA", "product": "11111"},
{"name": "BBBBB", "product": "22222"},
]
Now I want to index by json_field -> product field, because it is being used as identification. Then i want to create GinIndex like this:
class Meta:
indexes = [
GinIndex(name='product_json_idx', fields=['json_field->product'], opclasses=['jsonb_path_ops'])
]
When I try to create migration, I get error like this:
'indexes' refers to the nonexistent field 'json_field->product'.
How to create GinIndex that will be used for child attribute in Json Array?
Please don't use a JSONField [Django-doc] for well-structured data: if the structure is clear, like here where we have a list of objects where each object has a name and a product, it makes more sense to work with extra models, like:
class MyModel(models.Model):
# …
pass
class Product(models.Model):
# …
pass
class Entry(models.Model):
my_model = models.ForeignKey(MyModel, on_delete=models.CASCADE)
name = models.CharField(max_length=255)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
This will automatically add indexes on the ForeignKeys, but will also make querying simpeler and usually more efficient.
While databases like PostgreSQL indeed have put effort into making JSON columns easier to query, aggregate, etc. usually it is still beter to perform database normalization [wiki], especially since it has more means for referential integrity, and a lot of aggregates are simpeler on linear data.
If for example later a product is removed, it will require a lot of work to inspect the JSON blobs to remove that product. This is however a scenario that both Django and PostgreSQL databases cover with ON DELETE triggers and which will likely be more effective and safe when using the Django toolchain for this.

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 Serializers many to many relationship

I am working in a small project using Angularjs and Django. I have two different models with its corresponding end points.
class Skills(models.Model):
skills = models.CharField(max_length=100)
def __str__(self):
return self.skills
class Project(models.Model):
...
required_skills = models.ManyToManyField(
Skills, verbose_name="list of skills")
..
This is a copy of my skills end point
[
{
"id": 1,
"skills": "Css"
},
{
"id": 2,
"skills": "HTML5"
},
{
"id": 3,
"skills": "Java"
}
]
On the front page I have a form that uses $http.post service to send data to the database. When I project is created, it might require one skill or multiple skills. Here is where my confusion come, I can store I foreign key by sending the pk which in this case is id. However, since I am dealing with a many to many relationship, I can not figure out what is the best way to proceed. I have read the documentation and some online resources with any success. Also, the form has an dropdown menu with the list of skills.I got this values using $http.get. Please anyone has any idea of how to solve this issue?
http://www.django-rest-framework.org/api-guide/relations/
Serializing ManyToMany in Django Rest Framework (2.3.5) throws ValueError
I had to deal with a similar situation recently.
The way I approached the problem was to override the create method in the serializer to look like:
def create(self, validated_data):
serviceOffers_data = validated_data.pop('serviceOffers')
def extract_id(s):
return s['id']
clientfile = Clientfile.objects.create(**validated_data)
clientfile.serviceOffers.add(*ServiceOffer.objects.filter(id__in=map(extract_id, serviceOffers_data)))
return clientfile
My POST data looks like so:
{
name: 'Hello',
serviceOffers: [
{
id: 1,
name: 'service offer 1'
},
{
id: 2,
name: 'service offer 2'
}
]
}
The extract_id function isn't mandatory. I used it because of the structure of the incoming Json. You could as well just send the list of pk's and get rid of extract_ids.