MongoEngine Unexpectantly Changing Field Value - mongoengine

I am trying to simulate a sequence in a mongoengine document class. I have overwritten the init function to find the next contiguous value and am setting it in the document. For some reason the number gets incremented every time I add another object or call the objects.all() function. Can anyone tell me what is going on?
Here is the code:
from mongoengine import connect, Document, StringField, IntField
class MyDocument(Document):
number = IntField(unique=True, required=True)
name = StringField()
def __init__(self, *args, **kwargs):
super(MyDocument, self).__init__(*args, **kwargs)
num_objs = MyDocument.objects.count()
self.number = 1 if num_objs == 0 else num_objs + 1
connect('test-database')
MyDocument.drop_collection()
pj = MyDocument(name='foo').save()
print((pj.number, pj.name)) # pj.number is 1?
print([(pj.number, pj.name) for pj in MyDocument.objects.all()]) # pj.number is 2?
pj = MyDocument(name='bar').save()
print([(pj.number, pj.name) for pj in MyDocument.objects.all()]) # both pj.numbers are 3?
which outputs:
(1, 'foo')
[(2, 'foo')]
[(3, 'foo'), (3, 'bar')]

I'm still not sure what the problem was that caused the side-effect, but I was able to fix it using a pre_save signal:
from mongoengine import connect, signals, Document, StringField, IntField
class MyDocument(Document):
number = IntField(unique=True, required=True)
name = StringField()
#classmethod
def pre_save(cls, sender, document, **kwargs):
num_objs = MyDocument.objects.count()
document.number = 1 if num_objs == 0 else num_objs + 1

MongoEngine provides a SequenceField to support that use case. It should fit your need.
As to why your implementation is not correct, its because the constructor is called every time a new object is constructed as in
MyDocument(name='whatever')
But the constructor is also called everytime an object is fetched from the database, (e.g MyDocument.objects.all() or MyDocument.objects.first()) in fact behind the scenes, pymongo provides a dict that MongoEngine turns into a MyDocument instance by using the constructor.

Related

Python (+Django) : use #property in success_url KO . while using it with get_success_url is OK (class based view)

I found a workaround for my issue but I need to know why the first above case doesn't work.
I need to pass a parameter (reman_pk) to my view but when I try :
class RepairCreateView(LoginRequiredMixin, CreateView):
#property
def reman_pk(self):
return int(self.kwargs['reman_pk'])
[...]
success_url = reverse_lazy(
'reman:update-reman', kwargs={'pk': reman_pk})
[...]
... I got an error
django.urls.exceptions.NoReverseMatch: Reverse for 'update-reman' with keyword arguments '{'pk': <property object at 0x10c20bbd0>}' not found. 1 pattern(s) tried: ['reman/update/(?P[0-9]+)/$']
But when in the same class based view I use :
def get_success_url(self, **kwargs):
if kwargs != None:
return reverse_lazy('reman:update-reman', kwargs={'pk': self.reman_pk})
... it's OK : an int is well passed in my URL.
I tried to pass int(reman_pk) in the first method ... not better.
I've already use #property in the past and always got a value (int/str) and not property object.
EDIT (FULL views.py)
success_url = reverse_lazy...is commented. I must use def get_success_url( ... instead. Otherwise I get the above mentioned error.
class RepairCreateView(LoginRequiredMixin, CreateView):
#property
def reman_pk(self):
return int(self.kwargs['reman_pk'])
# success_url = reverse_lazy(
# 'reman:repairs-list', kwargs={'pk': reman_pk})
success_message = "Nouvelle réparation créée"
form_class = RepairCreateForm
template_name = 'reman/repair_create_form.html'
def get_context_data(self, *args, **kwargs):
context = super(RepairCreateView, self).get_context_data(
*args, **kwargs)
context['title'] = 'Nouveau Repair'
context['pk'] = self.reman_pk
return context
def get_initial(self):
reman = Reman.objects.get(pk=self.reman_pk)
return {'reman': reman}
def get_success_url(self, **kwargs):
return reverse_lazy('reman:repairs-list', kwargs={'pk': self.reman_pk})
This is not related to django, this is related to python in general. When you want to access a class property within the class you always have to call self before!
class Tree:
fruits = 5
#property
def leafes(self):
return self.fruits * 5
def show_tree(self):
print(self.fruits)
print(self.leafes)
print(leafes) # THIS LINE WOULD ERROR
Edit after comment of OP
I don't know how to phrase this properly. Anyhow this keeps being a problem related to python and not to django. The reason is how classes work.
You probably know the def __init__(self): function. That is called when the class gets instanciated. After that function got called your class can use all the self attributes (class attributes). But class attributes like my fruits = 5 get assigned even before that def __init__(self) method is called. So all your assignments directly inside the body of the class do not have self yet.
class Tree:
fruits = 5
def __init__(self):
self.twigs = 10
self.weight = self.twigs + self.fruits # THIS WORKS
class Tree:
fruits = 5
weight = self.twigs + fruits # THIS DOES NOT WORK
def __init__(self):
self.twigs = 10
Last example does not work because at the moment you want to assign weight = self.twigs + fruits your class's __init__ function was not called yet. So you can not use self at that place.

I am getting "AttributeError: 'str' object has no attribute

I am getting "AttributeError: 'str' object has no attribute 'model'" on line 26. I can not figure out why? not sure how to correct?
import time
import threading
def test(name,name2):
print(name)
print(name2)
car.show(name)
car.color(name2)
time.sleep(30)
class car():
def __init__(self, model, color):
self.model = model
self.color = color
def show(self):
print("Model is", self.model )
print("color is", self.color )
audi = car("audi a4", "blue")
ferrari = car("ferrari 488", "green")
acura = car("acura" , "black")
BMW = car("BMW" , "blue")
Cadillac = car("Cadillac", "green")
f = open("/home/stuff/script/QT/car.txt", "r") #In car.txt file has car model and color list line by line
threads = []
for x in range (5):
name=(f.readline())
name=name.strip()
name2=(f.readline())
name2=name2.strip()
info = threading.Thread (target=test(name,name2))
threads.append(info)
info.start()
x= +x;
f.close()
Now come to look at it your code has multiple problems.
Firstly in your test method make the following changes
Right now your car class doesn't have a color method. It only has a color field which you can access
def test(name,name2):
print(name)
print(name2)
some_car = car(name, name2)
some_car.show()
print(some_car.color)
time.sleep(30)
if you somehow want to pass arg to the show method use the following method signature
def show(self, name)
the first param if class method is reference to the same object.
Refer here for more info on Python3 classes
Indent your show method, Currently it is outside of the class car and hence most probably the problem is because you are passing the self param to a function and not a class method. So self is being evaluated to a string and when you are printing it using self.model it is throwing the exception
you have problem in these lines:
car.show(name)
car.color(name2)
car is class (and btw the convention is to use Car)
car.show is just a function and you pass it a string,
for example 'car.show('blue')' which causes self.model to fail becasue 'blue'.model is faulty.
I am pretty sure you meant to instantiate a car in your test function
with:
the_car = car(name, name2)
the_car.show()

django-import-export how to skip import some rows based on current user login?

Actually started using django-import-export latest version.
Wanted to know where exactly we can override to skip certain rows of the csv from being imported based on current user or the domains from a list of domains he can import data from the csv. How exactly to customize which of the methods to override and how?
In my ModelResource, I have created the list of domains for the current user, and which method of the import-export do I check this and skip the rows from being imported?
class MailboxResource(resources.ModelResource):
mdomain_list = []
def import_data(self, *args, **kwargs):
# make changes to csv
super(MailboxResource, self).before_import(*args, **kwargs)
muser_id = kwargs['user'].id
muser = kwargs['user']
# import for all domains
if muser.is_superuser:
pass
# import for domains belonging to the hierarchy
elif muser is not None:
exist = muser.groups.filter(name='customers').exists()
self.mdomain_list.append(Domain.objects.filter(
customer__in=Customer.objects.filter(
email=muser)))
def skip_row(self, instance, original):
mdomain = instance.email.partition('#')[2]
for x in self.mdomain_list:
print(type(x[0].domain), 'xxx', x[0].domain, mdomain)
if x[0].domain == mdomain:
return False
else:
return True
Hence customer should be able to import data from the CSV only for domains that belong to him and skip all other rows from the CSV, which don't exist in the list.
CSV:
id,name,email,domain,
1,ABC pvt.ltd,abc#zinn.com,zinn.com,
2,XTD,xtd#ggg.com,ggg.co.in,
3,RTG,tiger#goa.com,goa.com
If customer doesn't own ggg.com domain, only 1st and 3rd row should get added to the table via import. How can this be achieved?
Using python 3.
Checked the document here: https://django-import-export.readthedocs.io/en/stable/api_resources.html#import_export.resources.Resource.skip_row but couldn't make out much for my use-case.
You can use the skip_row(...)--(Doc) method, as you mentioned.
But, the skip_row(...) method doesn't provide any hooks to the request.user, so, we are doing a simple hack to get the requested user in skip_row() by overriding the import_data(...)
from import_export.resources import ModelResource
class BookResource(ModelResource):
class Meta:
model = Book
def import_data(self, *args, **kwargs):
self.user = kwargs.get("user") # Here, we are assigning the requested user to the `ModelResource` object.
return super().import_data(*args, **kwargs)
def skip_row(self, instance, original):
# You can access the current logged-in user by `self.user`
# and later, do some logical operations
# and at last, return either `True` or `False`
pass
Upate 1
I have updated the skip_row(...) method to test whether the skip is working properly or not.
class BookResource(ModelResource):
count = 1
class Meta:
model = Book
def import_data(self, *args, **kwargs):
self.user = kwargs.get("user")
return super().import_data(*args, **kwargs)
def skip_row(self, instance, original):
skip = self.count % 2 == 0
self.count += 1
return skip

Python - How to return a the details of each Room instance in a list, from inside of a Map instance

I am working on a small text adventure in python, and am attempting to use classes. I'm not very well versed in OOP and although I feel like I'm slowly gaining a greater understanding...I know that I still have a ways to go.
This is my room class
#!usr/bin/env python3
"""
A room class and a method to load room data from json files
"""
import json
class Room():
def __init__(
self,
id = "0",
name = "An empty room",
desc = "There is nothing here",
items = {},
exits = {},
):
self.id = id
self.name = name
self.desc = desc
self.items = items
self.exits = exits
def __str__(self):
return "{}\n{}\n{}\n{}".format(self.name, self.desc, self.items, self.exits)
# Create method to verify exits
def _exits(self, dir):
if dir in self.exits:
return self.exits[dir]
else:
return None
def north(self):
return self._exits('n')
def south(self):
return self._exits('s')
def east(self):
return self._exits('e')
def west(self):
return self._exits('w')
# Create method to get room info from json file
def get_room(id):
ret = None
with open("data/{}.json".format(str(id)) , "r") as f:
jsontext = f.read()
d = json.loads(jsontext, strict = False)
d['id'] = id
ret = Room(**d)
return ret
This is my map class
#!/usr/bin/env python3
from rooms import *
"""
Map class used to build a map from all of the rooms
"""
class Map():
def __init__(self, rooms = {}):
self.rooms = rooms
def __str__(self):
return map(str, rooms)
def build_map(id, num_of_rooms):
rooms = {}
room_count = 0
while room_count < num_of_rooms:
rooms[id] = get_room(id)
id += 1
room_count += 1
return rooms
a_map = Map(build_map(1, 3))
def test_map(map):
return map.rooms
print(test_map(a_map))
I'm not understanding why test_map only returns a list of objects, and was wondering how I might be able to receive the actual list of rooms so that I can confirm that they were created properly. I'm sure that I'm just going about this the COMPLETE wrong way...which is why I've come here with the issue.
For general information about __str__ and __repr__, Check out this answer.
In this case, here's what's happening:
Your __str__ function on Map doesn't return a string, it returns a map object. __str__ must return a string.
That's not causing an error because the function isn't getting called, here: test_map returns the given Map's rooms attribute, which is a dictionary. If you tried to do print(a_map) you'd get an exception.
Dictionaries have their own __str__ method, which is getting called here but the dictionary's __str__ method calls __repr__ on its members, which you haven't defined. (See the linked answer for details on why this is so.)
When you haven't defined a __repr__ for your class, you get the __repr__ from object, which is why your print(test_map(a_map)) gives you output like {1: <__main__.Room instance at 0x7fca06a5b6c8>, 2: <__main__.Room instance at 0x7fca06ac3098>, 3: <__main__.Room instance at 0x7fca06ac36c8>}.
What I'd suggest:
Write __repr__ functions for both Map and Room.
Have Map.__repr__ return a string that indirectly relies on Room.__repr__, something like the following:
def __repr__(self):
return '<Map: {}>'.format(self.rooms)
This isn't maybe the best long-term approach - what if you have a map with a hundred rooms? - but it should get you going and you can experiment with what works best for you.

Return output of the function executed 'on_click'

How to get the output of the function executed on_click by ipywidgets.Button
outside the function to use in next steps? For example, I want to get back the value of a after every click to use in the next cell of the jupyter-notebook. So far I only get None.
from ipywidgets import Button
def add_num(ex):
a = b+1
print('a = ', a)
return a
b = 1
buttons = Button(description="Load/reload file list")
a = buttons.on_click(add_num)
display(buttons)
print(a)
The best way that I have found to do this type of thing is by creating a subclass of widgets.Button and then add a new traitlet attribute . That way you can load the value(s) that you want to perform operations on when you create a new button object. You can access this new traitlet attribute whenever you want inside or outside of the function. Here is an example;
from ipywidgets import widgets
from IPython.display import display
from traitlets import traitlets
class LoadedButton(widgets.Button):
"""A button that can holds a value as a attribute."""
def __init__(self, value=None, *args, **kwargs):
super(LoadedButton, self).__init__(*args, **kwargs)
# Create the value attribute.
self.add_traits(value=traitlets.Any(value))
def add_num(ex):
ex.value = ex.value+1
print(ex.value)
lb = LoadedButton(description="Loaded", value=1)
lb.on_click(add_num)
display(lb)
Hope that helps. Please comment below if this does not solve your problem.

Resources