Im trying to extend the base functionality of the Document class like the following:
class DocumentExtended(Document):
meta = {'allow_inheritance': True}
class User(DocumentExtended):
name = StringField()
User(name="John Smith").save()
The purpose is that I want to add some extra methods to DocumentExtended (but I've omitted those for brevity)
The problem is that the document does not get saved.
If I do
class User(Document):
name = StringField()
User(name="John Smith").save()
it does get saved so I know it should work
Is there some weird trick I need to do to be able to extend the mongoengine.Document class and be able to save the models to the database?
After 2 hours of not understanding I finally read the docs
The DocumentExtended class must set meta = {'abstract': True}
class DocumentExtended(Document):
meta = { 'abstract': True }
Related
I'm writing a web scraper to get information about customers and appointment times to visit them. I have a class called Job that stores all the details about a specific job. (Some of its attributes are custom classes too e.g Client).
class Job:
def __init__(self, id_=None, client=Client(None), appointment=Appointment(address=Address(None)), folder=None,
notes=None, specific_reqs=None, system_notes=None):
self.id = id_
self.client = client
self.appointment = appointment
self.notes = notes
self.folder = folder
self.specific_reqs = specific_reqs
self.system_notes = system_notes
def set_appointment_date(self, time, time_format):
pass
def set_appointment_address(self, address, postcode):
pass
def __str__(self):
pass
My scraper works great as a stand alone app producing one instance of Job for each page of data scraped.
I now want to save these instances to a Django database.
I know I need to create a model to map the Job class onto but that's where I get lost.
From the Django docs (https://docs.djangoproject.com/en2.1/howto/custom-model-fields/) it says in order to use my Job class in the Django model I don't have to change it at all. That's great - just what I want. but I can't follow how to create a model that maps to my Job class.
Should it be something like
from django.db import models
import Job ,Client
class JobField(models.Field):
description = "Job details"
def __init__(self, *args, **kwargs):
kwargs['id_'] = Job.id_
kwargs['client'] = Client(name=name)
...
super().__init__(*args, **kwargs)
class Job(models.Model):
job = JobField()
And then I'd create a job using something like
Job.objects.create(id_=10101, name="Joe bloggs")
What I really want to know is am I on the right lines? Or (more likely) how wrong is this approach?
I know there must be a big chunk of something missing here but I can't work out what.
By mapping I'm assuming you want to automatically generate a Django model that can be migrated in the database, and theoretically that is possible if you know what field types you have, and from that code you don't really have that information.
What you need to do is to define a Django model like exemplified in https://docs.djangoproject.com/en/2.1/topics/db/models/.
Basically you have to create in a project app's models.py the following class:
from django import models
class Job(models.Model):
client = models.ForeignKey(to=SomeClientModel)
appointment = models.DateTimeField()
notes = models.CharField(max_length=250)
folder = models.CharField(max_length=250)
specific_reqs = models.CharField(max_length=250)
system_notes = models.CharField(max_length=250)
I don't know what data types you actually have there, you'll have to figure that out yourself and cross-reference it to https://docs.djangoproject.com/en/2.1/ref/models/fields/#model-field-types. This was just an example for you to understand how to define it.
After you have these figured out you can do the Job.objects.create(...yourdata).
You don't need to add an id field, because Django creates one by default for all models.
I have this 3 models:
class MyFile(models.Model):
file = models.FileField(upload_to="files/%Y/%m/%d")
def __unicode__(self):
"""."""
return "%s" % (
self.file.name)
class ExampleModel(models.Model):
attached_files =models.ManyToManyField(MyFile)
main_model = models.ForeignKey(MainModel)
class MainModel(models.Model):
attached_files =models.ManyToManyField(MyFile)
And my admin.py as follows:
class ExampleModelAdminInline(admin.TabularInline):
model = ExampleModel
extra = 2
class MainModelAdmin(admin.ModelAdmin):
inlines = [ExampleModelAdminInline]
Im using django-grapelli because it offer autocomplete lookups for many to many fields. However, Im not sure how to add this autocomplete lookup to a TabularInline admin. Can anyone explain me how to set up the attached_files field to have autocomplete lookups?
First you need to set the static method autocomplete_search_fields() in the Model you want to search from, in your case MyFile. From the docs we get:
class MyFile(models.Model):
#your variable declaration...
#staticmethod
def autocomplete_search_fields():
return ("id__iexact", "name__icontains",) #the fields you want here
You can also define GRAPPELLI_AUTOCOMPLETE_SEARCH_FIELDS instead of declaring the static method, like:
GRAPPELLI_AUTOCOMPLETE_SEARCH_FIELDS = {
"myapp": {
"MyFile": ("id__iexact", "name__icontains",)
}
}
Then you should add the lookup and raw fields to your desired admin class, considering its related Model (say, your ExampleModel) which is the one that has a ManyToManyField. You can also handle ForeignKey in a similar way. Also from the mentioned docs:
class ExampleModel(models.Model):
main_model = models.ForeignKey(MainModel) #some FK to other Model related
attached_files =models.ManyToManyField(MyFile) #the one with static decl
class MainModelAdmin(admin.ModelAdmin):
#your variable declaration...
# define raw fields
raw_id_fields = ('main_model','attached_files',)
# define the autocomplete_lookup_fields
autocomplete_lookup_fields = {
'fk': ['main_model'],
'm2m': ['attached_files'],
}
Remember to register both ends (your models) of the relationship to your admin.site, like this:
#the one with the m2m and the one with the lookup
admin.site.register(ExampleModel, MainModelAdmin)
You can also check this question to understand better.
Is there a quicker way to append a class name to an input field in Django Admin? e.g. I have a text field called 'markdown', and I want to add the class markdown to it, so I can easily apply a markdown editor in admin.
The result of this code is perfect. Just a bit of a PITA to apply this across all my admin forms..
I can't use formfield_override since not all my textareas are markdown.
class MyModelAdminForm(forms.ModelForm):
class Meta:
model = models.MyModel
widgets = {
'markdown': forms.Textarea(attrs={'class': 'markdown'})
}
class MyModelAdmin(admin.ModelAdmin):
form = MyModelAdminForm
Why not creating a string which will hold the class name for each input you want to give it a class and then pass that string to attrs's class key?
# forms.py
MARKDOWN = 'markdown'
class MyModelAdminForm(forms.ModelForm):
class Meta:
model = models.MyModel
widgets = {
'markdown': forms.Textarea(attrs={'class': MARKDOWN})
}
Now you can reuse MARKDOWN to other modelForms. Of course this does not solve the repetition but at least is less error prone.
Another solution is to use javascript (first include it via the class Media inside your MyModelAdmin class) and then inside it you should do something like that:
// myModelAdmin.js
const $ = django.jQuery;
$(function() {
$('your-selectors').addClass('markdown');
});
With django-rest-framework 3.0 and having these simple models:
class Book(models.Model):
title = models.CharField(max_length=50)
class Page(models.Model):
book = models.ForeignKey(Books, related_name='related_book')
text = models.CharField(max_length=500)
And given this JSON request:
{
"book_id":1,
"pages":[
{
"page_id":2,
"text":"loremipsum"
},
{
"page_id":4,
"text":"loremipsum"
}
]
}
How can I write a nested serializer to process this JSON and for each page for the given book either create a new page or update if it exists.
class RequestSerializer(serializers.Serializer):
book_id = serializers.IntegerField()
page = PageSerializer(many=True)
class PageSerializer(serializers.ModelSerializer):
class Meta:
model = Page
I know that instantiating the serializer with an instance will update the current one but how should I use it inside the create method of nested serializer?
Firstly, do you want to support creating new book instances, or only updating existing ones?
If you only ever wanted to create new book instances you could do something like this...
class PageSerializer(serializers.Serializer):
text = serializers.CharField(max_length=500)
class BookSerializer(serializers.Serializer):
page = PageSerializer(many=True)
title = serializers.CharField(max_length=50)
def create(self, validated_data):
# Create the book instance
book = Book.objects.create(title=validated_data['title'])
# Create or update each page instance
for item in validated_data['pages']:
page = Page(id=item['page_id'], text=item['text'], book=book)
page.save()
return book
Note that I haven't included the book_id here. When we're creating book instances we won't be including a book id. When we're updating book instances we'll typically include the book id as part of the URL, rather than in the request data.
If you want to support both create and update of book instances then you need to think about how you want to handle pages that are not included in the request, but are currently associated with the book instance.
You might choose to silently ignore those pages and leave them as they are, you might want to raise a validation error, or you might want to delete them.
Let's assume that you want to delete any pages not included in the request.
def create(self, validated_data):
# As before.
...
def update(self, instance, validated_data):
# Update the book instance
instance.title = validated_data['title']
instance.save()
# Delete any pages not included in the request
page_ids = [item['page_id'] for item in validated_data['pages']]
for page in instance.books:
if page.id not in page_ids:
page.delete()
# Create or update page instances that are in the request
for item in validated_data['pages']:
page = Page(id=item['page_id'], text=item['text'], book=instance)
page.save()
return instance
It's also possible that you might want to only support book updates, and not support creation, in which case, only include the update() method.
There are also various ways you could reduce the number of queries eg. using bulk create/deletion, but the above would do the job in a fairly straightforward way.
As you can see there are subtleties in the types of behavior you might want when dealing with nested data, so think carefully about exactly what behavior you're expecting in various cases.
Also note that I've been using Serializer in the above example rather than ModelSerializer. In this case it's simpler just to include all the fields in the serializer class explicitly, rather than relying on the automatic set of fields that ModelSerializer generates by default.
You can simply use drf-writable-nested.
It automatically make your nested serializers writable and updatable.
in you serializers.py:
from drf_writable_nested import WritableNestedModelSerializer
class RequestSerializer(WritableNestedModelSerializer):
book_id = serializers.IntegerField()
page = PageSerializer(many=True)
class PageSerializer(serializers.ModelSerializer):
class Meta:
model = Page
And that's it!
Also the library supports using only one of the create and update logics if you don't need both.
I'm using django to build an internal webapp where devices and analysis reports on those devices are managed.
Currently an abstract Analysis is defined like this:
class Analysis(models.Model):
project = models.ForeignKey(Project)
dut = models.ForeignKey(Dut) # Device Under Test
date = models.DateTimeField()
raw_data = models.FileField(upload_to="analysis")
public = models.BooleanField()
#property
def analysis_type(self):
s = str(self.__class__)
class_name = s.split('.')[-1][:-2] # Get latest name in dotted class name as remove '> at end
return AnalysisType.objects.get(name=class_name)
class Meta:
abstract = True
There are then a number of different analysis types that can be done on a device, with different resulting data.
class ColorAnalysis(Analysis):
value1 = models.FloatField()
value2 = models.FloatField()
...
class DurabilityAnalysis(Analysis):
value1 = models.FloatField()
value2 = models.FloatField()
...
...
Each such analysis is created from an Excel sheet posted by the operator. There exists an Excel template the operator fills in for each analysis type.
(The issue here is not if data input should be done in a web form, there are lots of reasons to choose the Excel path)
On a page on the website all analysis types should be listed along with a link to the corresponding Excel sheet template used to report that analysis.
Currently I have defined something like
class AnalysisType(models.Model):
name = models.CharField(max_length=256 )
description = models.CharField(max_length=1024,blank=True )
template = models.FileField(upload_to="analysis_templates")
but when I though about how I would link this data to the different analysis result model classes I though that what I want to do is to add this data as class attributes to each analysis type.
The problem is that the class attributes are already used by the django magic to define the data of each instance.
How do I add "class attributes" to django models? Any other ideas on how to solve this?
EDIT:
Now added the analysis_type property by looking up the class name. This requires no manual adding of a variable to each sub-class. Works fine, but still requires manual adding of an entry of AnalysisType corresponding to each sub-class. It would be nice if this could be handled by the class system as well. Any ideas?
How about a property or method that returns an AnalysisType dependent on an attribute in the particular Analysis subclass?
class Analysis(models.Model):
...
#property
def analysis_type(self):
return AnalysisType.objects.get(name=self.analysis_type_name)
class ColorAnalysis(Analysis):
analysis_type_name = 'color'
class DurabilityAnalysis(Analysis):
analysis_type_name = 'durability'