I tried using Celery Task & Django rest framework serializer in same class with multiple inheritance.
from celery import Task
class ReceiveSerializer(Task, serializers.Serializer):
def run(self, source, *args, **kwargs):
self.save()
def save(self, **kwargs):
# call long running save method
I got error,
File "<>\serializers.py", line 217, in <module>
class ReceiveSerializer(Task, serializers.Serializer):
File "<>\workspace\www\lib\site-packages\celery-3.1.20-py2.7.egg\celery\app\task.py", line 199, in __new_
_
tasks.register(new(cls, name, bases, attrs))
TypeError: Error when calling the metaclass bases
metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
The save method has to create long list of objects in db (some times more than 5 minutes). I don't want user to wait for long time.
Is there any way to do this.
Could it be possible using a Mixin?
class YourMixin:
# if you want to trigger the task on save:
def save(self, *args, **kwargs):
ret = super().save(*args, **kwargs)
some_task.apply_async((
self.__class__.__name__,
self.pk,
))
return ret
#task()
def some_task(model_name, model_id):
my_model = apps.get_model('django_app_name.{}'.format(model_name))
obj = my_model.objects.get(pk=model_id)
Related
thanks for tanking the time to look at this query.
I'm setting an ID field within one of my Django models. This is a CharField and looks like the following:
my_id = models.CharField(primary_key=True, max_length=5,
validators=[RegexValidator(
regex=ID_REGEX,
message=ID_ERR_MSG,
code=ID_ERR_CODE
)])
I would like to add a default/blank or null option that calls a global or class function that will cycle through the existing IDs, find the first one that doesn't exist and assign it as the next user ID. However, when I add the call blank=foo() I get an error code that the function doesn't exist.
Best,
pb
Edit1: I also tried using a separate utils file and importing the function, but (unsurprisingly) I get a circular import error as I need the call the class to get the objects.
Edit2 (Reply to Eugene): Tried that, solved the circular import but I'm getting the following error:
TypeError: super(type, obj): obj must be an instance or subtype of type
Previously my override of the save function worked perfectly:
def save(self, *args, **kwargs):
self.full_clean()
super(Staff, self).save(*args, **kwargs)
The custom id function:
def get_id_default():
from .models import MyObj
for temp_id in range(10_000, 100_000):
try:
MyObj.objects.get(my_id=str(temp_id))
except ObjectDoesNotExist:
break # Id doesn't exist
return str(hive_id)
Edit 3 (Reply to PersonPr7): Unfortunately, the kwargs doesn't seem to have my id in it. Actually, after having a print the kwargs dictionary comes back empty.
Save function:
def save(self, *args, **kwargs):
print(kwargs) # --> Returns {}
if kwargs["my_id"] is None:
kwargs["my_id"] = self.get_id_default()
self.full_clean()
super(Staff, self).save(*args, **kwargs)
Where the get_id_default is a class function:
def get_id_default(self):
for temp_id in range(10_000, 100000):
try:
self.objects.get(my_id=str(temp_id))
except ObjectDoesNotExist:
break # Id doesn't exist
return str(temp_id)
Solution1:
For those who are may be struggling with this in the future:
Create a utils/script .py file (or whatever you wanna call it) and create your custom script inside.
from .models import MyModel
def my_custom_default:
# your custom code
return your_value
Inside the main.models.py file.
from django.db import models
from .my_utils import my_custom_default
class MyModel(model.Model):
my_field = models.SomeField(..., default=my_custom_default)
Solution2: Create a static function within your Model class that will create your default value.
#staticmethod
def get_my_default():
# your logic
return your_value
# NOTE: Initially I had the function use self
# to retrieve the objects (self.objects.get(...))
# However, this raised an exception: AttributeError:
# Manager isn't accessible via Sites instances
When setting up your model give your field some kind of default i.e. default=None
Additionally, you need to override the models save function like so:
def save(self, *args, **kwargs):
if self.your_field is None:
self.my_field = self.get_my_default()
self.full_clean()
super().save(*args, **kwargs)
Try overriding the Model's save method and performing the logic there:
def save(self, *args, **kwargs):
#Custom logic
super().save(*args, **kwargs)
Edit:
You don't need to use **kwargs.
You can access your whole model from the save method and loop over objects / ids.
I just want to know where create method comes from in django.
I checked Model class, BaseManager class and so on.
I cannot find where create method is defined.
Anyone knows about it ?
https://github.com/django/django/blob/2d6179c819010f6a9d00835d5893c4593c0b85a0/django/
Short answer: In the QuerySet class.
This is defined in the QuerySet. Indeed, in the Manager, it is wrapped to the QuerySet source [GitHub]:
#classmethod
def _get_queryset_methods(cls, queryset_class):
def create_method(name, method):
def manager_method(self, *args, **kwargs):
return getattr(self.get_queryset(), name)(*args, **kwargs)
manager_method.__name__ = method.__name__
manager_method.__doc__ = method.__doc__
return manager_method
new_methods = {}
for name, method in inspect.getmembers(queryset_class, predicate=inspect.isfunction):
# Only copy missing methods.
if hasattr(cls, name):
continue
# Only copy public methods or methods with the attribute `queryset_only=False`.
queryset_only = getattr(method, 'queryset_only', None)
if queryset_only or (queryset_only is None and name.startswith('_')):
continue
# Copy the method onto the manager.
new_methods[name] = create_method(name, method)
return new_methods
The _get_queryset_methods is called when constructing the Manager class [GitHub]:
class Manager(BaseManager.from_queryset(QuerySet)):
pass
and the from_queryset will attach the QuerySet methods to the Manager class [GitHub]:
#classmethod
def from_queryset(cls, queryset_class, class_name=None):
if class_name is None:
class_name = '%sFrom%s' % (cls.__name__, queryset_class.__name__)
return type(class_name, (cls,), {
'_queryset_class': queryset_class,
**cls._get_queryset_methods(queryset_class),
})
This is done to implement for each method in the QuerySet a method in the Manager to prevent implementing the same logic multiple times. If you thus call .create on the .objects manager (or any other manager), it will call .get_queryset(), and then call that method (here create) on the given QuerySet that get_queryset returns.
The QuerySet thus has a method create that is implemented as [GitHub]:
def create(self, **kwargs):
"""
Create a new object with the given kwargs, saving it to the database
and returning the created object.
"""
obj = self.model(**kwargs)
self._for_write = True
obj.save(force_insert=True, using=self.db)
return obj
The goal is removing of duplicates from list field while saving model. For example creation in migration:
def migrate_model(apps, *args):
MyModel = apps.get_model('my_app.MyModel')
m = MyModel.objects.create(
array_field=['123','123'],
)
m.array_field # ['123']
I tried to overwrite save but it doesn't work
class MyModel(models.Model):
array_field = ArrayField(models.CharField(max_length=5))
def save(self, *args, **kwargs):
if self.array_field:
self.array_field = list(set(self.array_field))
super(MyModel, self).save(*args, **kwargs)
How can I do this?
Careful, the save() method is NOT called when using create() according to django docs.
Maybe that is causing you the problems, because your overriden save method doesn't actually gets called.
I have my serializers code set up as follows:
class ProductSerializer(serializers.ModelSerializer):
"""
* Serializes Products.
"""
def __init__(self, *args, **kwargs):
print kwargs.pop('product_passed')
super(ProductSerializer, self).__init__(*args, **kwargs)
Now, this serializer is being called from a view using the code below:
product_passed = Product.objects.get(pk=pk)
product_serializer = ProductSerializer(product_passed)
What I want to do is call the product_passed from my serializer init method. How can I do this? Right now, my method does not work.
Pass it like this:
product_serializer = ProductSerializer(product_passed, product_passed= product_passed)
And access it from kwargs: kwargs.get('product_passed')
I have the following code to delete a file:
from django.db import models
from django import forms
import os
class Document(models.Model):
docfile = models.FileField(upload_to='documents/%Y/%m/%d')
def __unicode__(self):
return '%s' % (self.docfile.name)
def delete(self, *args, **kwargs):
os.rmdir(os.path.join(settings.MEDIA_ROOT, self.docfile.name))
super(Document,self).delete(*args,**kwargs)
It manages to delete the objects I ask it to in my views.py but when I reupload a file of the same name it seems as though the original file still exists since I'll get "output_1.txt" instead of "output.txt".
This is the code I use to delete:
def delete_matrix():
documents = Document.objects.all()
documents.delete()
Am I not deleting the file from the database? Any help would be appreciated.
Your problem is that you are overriding the delete() method on the model but you are calling the delete method on the QuerySet returned by the default manager (Documents.object.all().delete()). These are 2 separate methods so there are 2 ways of fixing this.
1.In the delete method of the model, replace the line
os.rmdir(os.path.join(settings.MEDIA_ROOT, self.docfile.name))
by
os.remove(os.path.join(settings.MEDIA_ROOT, self.docfile.name))
AND, call the delete method for each object separately. Replace
Document.objects.all().delete()
with
documents = Document.objects.all()
for document in documents:
document.delete()
2.Replace the default manager to return a custom QuerySet which overrides the delete() method. This is explained in Overriding QuerySet.delete() in Django
Try this
document = Document.objects.get(pk=pk)
# if `save`=True, changes are saved to the db else only the file is deleted
document.docfile.delete(save=True)
here is another solution
def delete(self, *args, **kwargs):
os.remove(os.path.join(settings.MEDIA_ROOT, self.qr_code.name))
super().delete(*args, **kwargs)
You can use a much simpler code:
def delete(self, *args, **kwargs):
if self.docfile:
self.docfile.delete()
super().delete(*args, **kwargs)