Wagtail - Add data to streamfield CharBlock on save() - wagtail

I'm trying to automatically fill in a specific charblock on a streamfield I have created on save and I can get it to change but not save because when the page refreshes the new data is not there.
def save(self, *args, **kwargs):
if kwargs.get('update_fields'):
if self.auto_update:
for product in self.product_details.raw_data:
product_name = product['value']['product_name']
product_url = product['value']['product_url']
# I'm trying to set the field data here
product['value']['product_snippet_url'] = 'Test'
# when I print, I see the 'Test' value in the dict
print(product)
else:
# Do stuff on publish()
pass
return super().save(*args, **kwargs)
# Streamfield Block
class ProductNameAndUrlBlock(blocks.StructBlock):
product_name = blocks.CharBlock(required=True, help_text='Add product name')
product_url = blocks.URLBlock(required=True, help_text='Add Product URL')
product_snippet_url = blocks.CharBlock(required=False, help_text='Link to snippet')
The above code is for testing purposes, my end goal is to insert the url of the related snippet there and then use JS to turn it into a button so when someone clicks on it, it links to the snippet page.

Writing back to the stream's raw_data property will not reliably save the data. raw_data is a "backup" copy of the data as retrieved from the database, in a simplified format - for example, a PageChooserBlock value is stored there as a plain ID rather than a Page object. Once data is read or written through the stream's standard methods (e.g. looping over for block in self.product_details or writing self.product_details[0] = ...), the raw_data entry is treated as stale and not looked at again.
The correct approach is to read and write the stream itself as a list, rather than going through raw_data:
for product in self.product_details:
product_name = product.value['product_name']
product_url = product.value['product_url']
# I'm trying to set the field data here
product.value['product_snippet_url'] = 'Test'

Related

Django, handling m2m change signals over a model with multi ManyToManyField

I have a model M contains three manytomanyfields(a,b,c) to three different models in django.
class M (models.Model):
name = models.CharField()
a = models.ManyToManyField(A)
b = models.ManyToManyField(B)
c = models.ManyToManyField(C)
Now, I want to handle any changes on my model M and send an HttpRequest to an url correspondingly.
as you know in post-save signal of model M the values of attributes for a,b and c are not set yet because they will be set when saving model M has been finished.(the rule of ManyToManyField)
I write a single handler for my model to handle any changes(either create a new instance or update a field/fields)
#receiver(post_save, sender=M)
#receiver(m2m_changed, sender=M.a.through)
#receiver(m2m_changed, sender=M.b.through)
#receiver(m2m_changed, sender=M.c.through)
def M_changes_handler(sender, **kwargs):
is_instance_set_compeletly(kwargs['instance']):
#send_and_HttpRequest(url, data = instance)
def is_instance_set_compeletly(kwargs['instance']):
if M.a.all() is not None and M.b.all() is not None and
M.c.all()is not None
flag = True
else:
flag = False
return flag
Now consider an update request (which changes all fields) on model M will send signals for all fields a,b and c; so three httpRequests would send to my sender with three different versions of instance! on the other hand when user just changes a field for example b of the model M this function will send only one httpRequest.
I want to handle this problem dynamically and just send one request per ANY TYPE of CHANGE on my model M.
I was wondering if somebody could help me :)
*Note the code above is just a draft and may contains syntax error so ignore them ;)
Update: My problem solved!
The process of saving a model containing m2mfields is interesting!
First of all the fields of M which are not m2m fields type e.g. are char field, foreign key or etc. had been set before calling post_save. So in post_save their values are updated
Although m2mfields wont be set till the saving of the model would be finished. Then m2mchange signals called to set m2mfields
The only way to have an updated version of your model is to overriding form save in admin.py because the "form save" will be called after finishing all m2mfields have been set
Solution:
For all stack-overflow users and Dear Blake Gibbs
If a model contains manytomany fields and you want to access all the data of a saved record, you cannot access those m2m fields because they will save after finishing the process of saving model(consider M in my example), then assign that Id to other self-created m2m table to bind that Id to m2m fields (e.g. A in my example). so overriding the save method of a model in admin.py does not work in this case.
I simply override the save function of my form in Forms.py
class MForm(forms.ModelForm):
class Meta:
model = M
exclude = []
def save(self, commit=True):
query = ""
old_instance = super(MForm, self).save(commit=False)# if you need the old instance
#otherwise return super(MForm, self).save(commit=True)
instance = super(MForm,self).save(commit=True)
in this case the "instance" is really saved completely and you can access m2m fields by using ".all()"
hope it helped!
you can hold previously instance of M and in post_save with new M check field have changed according to that you can send request.

Django Rest Framework: Assign image to model field from uploaded folder

I am HTTP posting a .zip File to my Django app via Django - Rest - Framework. This zip File is a Folder that contains several files, among them an image. I would like to extract the folder once its uploaded, select the image and assign it to the model. Is this possible? If not, maybe I can write a property that gets the thumbnail image? I want to be able to show all the thumbnails in a gallery later.
Something like:
class FUploadSerializer(serializers.ModelSerializer):
file = serializers.FileField()
class Meta:
model = FUpload
fields = ('created','file')
class FUpload(models.Model):
created = models.DateTimeField(auto_now_add=True)
file = models.FileField(upload_to=get_path)
thumbnail = ImageField() ??? # get from uploaded folder
EDIT
I tried the following, but am getting: "image": [
"The submitted data was not a file. Check the encoding type on the form."
] . Am I thinking about this completely backwards? Is it "better" to let the client handle sending the image and .zip folder separately (data is sent from a script)? Is the ModelViewSet the right place to handle this?
class Thumbnail(models.Model):
image = models.ImageField()
file = models.FileField()
class ThumbnailViewSet(viewsets.ModelViewSet):
queryset = Thumbnail.objects.all()
serializer_class = ThumbnailSerializer
parser_classes = (MultiPartParser, FormParser,)
def perform_create(self, serializer):
file = self.request.FILES['file']
zf = zipfile.ZipFile(file)
content_list = zf.namelist()
imgdata = zf.open(content_list[0])
serializer.save(image=imgdata)
class ThumbnailSerializer(serializers.ModelSerializer):
file = serializers.FileField()
image = serializers.ImageField(allow_empty_file=True, required=False)
class Meta:
model = Thumbnail
fields = ('file', 'image')
In order to make this work, you are going to need to implement your own custom parser that can handle the incoming zip file, extract it, and assign some of the files to specific fields.
class ZipParser(FileUploadParser):
"""
Parses an incoming ZIP file into separate fields.
The ZIP file is expected to be the only data passed in the request,
use the `MultiPartParser` as a base if it will be passed in using form
data instead.
"""
media_type = 'application/zip' # only needed for `FileUploadParser`
def parse(self, stream, media_type=None, parser_context=None):
import io
import zipfile
parsed = super(ZipParser, self).parse(
self,
media_type=media_type,
parser_context=parser_context
)
data = parsed.files
data['zip_file'] = data['file'] # Keep the original zip file as `zip_file`
parsed_file = data['file'] # Also store a reference to the original file so it can be used
# Try to parse the zip file
# This should also handle proper file handling (such as not passing a zip file)
with zipfile.ZipFile(parsed_file) as zip_file:
# Get the `bytes` object for a file using `read(file_name)`
image_data = zip_file.read('image.png') # Replace `image.png` with the image file
file_data = zip_file.read('info.txt')
# DRF expects that files are using an IO compatible object, so we need `BytesIO` here
image_stream = io.BytesIO(image_data)
file_stream = io.BytesIO(file_data)
# The changed files should go back onto the files dictionary
data['image'] = image_stream
data['file'] = file_stream
return parsed
You can alternatively do this outside of a parser before the data is passed into a serializer if you are using your own custom view.

wxPython: get data in variables from Google Spreadsheet and also from user, work with all variables, return results to user

I hope I didn't miss any topic that could answer my problem. I'm here now because I'm terribly frustrated and tired with the following task:
- I have a Spreasheet with Drive.Google with lots of data in it
- I would like to create an application with wxPython that would pull data from this spreeadsheet (in the most easy way possible)
- Would also like to get multiple data from a user who will access this application through a nice interface (panel aka window)
- The multiple data introduced by the user should be able to work with the data pulled out from the Spreasheet. For example to see if the data introduced by the user is in the Spreadsheet or not and also some other operations with the next data introduced by the user.
- Finally and most importantly show the results to the user (later I would also like to add some functions to save somehow the results)
I hope I managed to express clearly what I would like to do. Now I'm new to Google API's, Python adn wxPython, but I have experience with C++ , php, html .
I've spent 2 weeks now with discovering Drive.Google and learning Python and wxPython. I did follow all tuturials on these, made my notes, read tones of stackoverflow questions-answers, wiki.wxpython.org etc. I learn every single day and I can do now many things separately but to have all functions like I described above I just couldn't work out how to do. At least please orient me in the direction. Awfel lot of times I spend hours doing examples and getting nowhere. I have Python, wxPython extention, GoogleAppEngine Launcher and even pyCharm demo. Please be kind. This is my first question ever.
here's the mess I made so far combining relevant examples:
import wx
import gdata.docs
import gdata.docs.service
import gdata.spreadsheet.service
import re, os
class Form(wx.Panel):
def __init__(self, *args, **kwargs):
super(Form, self).__init__(*args, **kwargs)
self.createControls()
self.bindEvents()
self.doLayout()
self.spreasht()
def createControls(self):
self.logger = wx.TextCtrl(self, style=wx.TE_MULTILINE|wx.TE_READONLY)
self.saveButton = wx.Button(self, label="Elvegzes")
self.nameLabel = wx.StaticText(self, label="type Name1:")
self.nameTextCtrl = wx.TextCtrl(self, value="type here")
self.name2Label = wx.StaticText(self, label="type Name2:")
self.name2TextCtrl = wx.TextCtrl(self, value="type here")
def bindEvents(self):
for control, event, handler in \
[(self.saveButton, wx.EVT_BUTTON, self.onSave),
(self.nameTextCtrl, wx.EVT_TEXT, self.onNameEntered),
(self.nameTextCtrl, wx.EVT_CHAR, self.onNameChanged)]:
control.Bind(event, handler)
def doLayout(self):
raise NotImplementedError
def spreadsht(self):
gd_client = gdata.spreadsheet.service.SpreadsheetsService()
gd_client.email = 'my email address'
gd_client.password = 'my password to it'
gd_client.source = 'payne.org-example-1'
gd_client.ProgrammaticLogin()
q = gdata.spreadsheet.service.DocumentQuery()
q['title'] = 'stationcenter'
q['title-exact'] = 'true'
feed = gd_client.GetSpreadsheetsFeed(query=q)
spreadsheet_id = feed.entry[0].id.text.rsplit('/',1)[1]
feed = gd_client.GetWorksheetsFeed(spreadsheet_id)
worksheet_id = feed.entry[0].id.text.rsplit('/',1)[1]
al1 = raw_input('Name1: ')
print al1
al2 = raw_input('Name2: ')
print al2
rows = gd_client.GetListFeed(spreadsheet_id, worksheet_id).entry
for row in rows:
for key in row.custom:
if al1 == row.custom[key].text:
print ' %s: %s' % (key, row.custom[key].text)
def onColorchanged(self, event):
self.__log('User wants color: %s'%self.colors[event.GetInt()])
def onReferrerEntered(self, event):
self.__log('User entered referrer: %s'%event.GetString())
def onSave(self,event):
self.__log('User clicked on button with id %d'%event.GetId())
def onNameEntered(self, event):
self.__log('User entered name: %s'%event.GetString())
def onNameChanged(self, event):
self.__log('User typed character: %d'%event.GetKeyCode())
event.Skip()
def onInsuranceChanged(self, event):
self.__log('User wants insurance: %s'%bool(event.Checked()))
# Helper method(s):
def __log(self, message):
''' Private method to append a string to the logger text
control. '''
self.logger.AppendText('%s\n'%message)
class FormWithSizer(Form):
def doLayout(self):
''' Layout the controls by means of sizers. '''
boxSizer = wx.BoxSizer(orient=wx.HORIZONTAL)
gridSizer = wx.FlexGridSizer(rows=5, cols=2, vgap=10, hgap=10)
# Prepare some reusable arguments for calling sizer.Add():
expandOption = dict(flag=wx.EXPAND)
noOptions = dict()
emptySpace = ((0, 0), noOptions)
# Add the controls to the sizers:
for control, options in \
[(self.nameLabel, noOptions),
(self.nameTextCtrl, expandOption),
(self.allomas2Label, noOptions),
(self.allomas2TextCtrl, expandOption),
emptySpace,
(self.saveButton, dict(flag=wx.ALIGN_CENTER))]:
gridSizer.Add(control, **options)
for control, options in \
[(gridSizer, dict(border=5, flag=wx.ALL)),
(self.logger, dict(border=5, flag=wx.ALL|wx.EXPAND,
proportion=1))]:
boxSizer.Add(control, **options)
self.SetSizerAndFit(boxSizer)
class FrameWithForms(wx.Frame):
def __init__(self, *args, **kwargs):
super(FrameWithForms, self).__init__(*args, **kwargs)
notebook = wx.Notebook(self)
form2 = FormWithSizer(notebook)
notebook.AddPage(form2, 'CALTH')
self.SetClientSize(notebook.GetBestSize())
if __name__ == '__main__':
app = wx.App(0)
frame = FrameWithForms(None, title='Relevant title˝')
frame.Show()
app.MainLoop()
THANK YOU AGAIN!!!!!!!!!!!
First, make sure you can download the data you want with just Python. Then create a wxPython GUI with a single button. In that button's handler, have it call the script that can download the data you want.
If that causes your GUI to become unresponsive, then you'll need to use a thread to do the downloading. I recommend the following articles if that's the case:
http://www.blog.pythonlibrary.org/2010/05/22/wxpython-and-threads/
http://wiki.wxpython.org/LongRunningTasks
Okay, so now you have the data downloading appropriately. Now you add a grid widget or a listctrl / object list view widget. Pick one of those. I prefer object list view, which you can read about here. Then in your button handler you can call your downloader script or thread and when that's done, you can load the widget with that data. If you're using a thread, then the thread will have to call the next step (i.e. the widget loading bit).
Now you should have your data displayed. All that's left is making it look pretty and maybe putting the downloading part into a menu item.

How to make references between expando models?

Update
This was my best effort creating the following scheme
user = self.auth.store.user_model.create_user(email,
password_raw=newpasswd)
if not user[0]: # user is a tuple
return user[1] # Error message
else:
# User is created, let's try making the references
okuser = auth_models.User.get_by_id(long(user[1].key.id()))
okuser.sponsor = auth_models.User.get_by_id(long(sponsor_id)).auth_ids
Original question
How can I make a selfreference with an expando class to indicate which User is the "sponsor" of which? The "sponsor" is the one who invited the new User so at creation we must store that and it would be much neater to store it as a referenceproperty than a string or a stringlist.
I can create a new user but I don't know how to make a reference so that I can tell for one User who another User is who is the sponsor of the first user and I suppose a way to model this is with selfreferenceproperty since both objects are users but the complication is that it is an expando model so I don't know how to use the reference property. Could you tell me how to do it or give me a clue how I can solve this problem in the best way?
user = self.auth.store.user_model.create_user(email,
password_raw=newpasswd)
if not user[0]: # user is a tuple
return user[1] # Error message
else:
# User is created, let's try making the reference
okuser = auth_models.User.get_by_id(user[1].key.id())
okuser.sponsor = db.SelfReferenceProperty(User,
collection_name='matched_images', verbose_name='Sponsor')
I don't know how to do the last part, to store the actual referenceproperty with an epando model. How can it be done?
Update
It seems it can't be done:
NotImplementedError: Property sponsor does not support <class 'google.appengine.ext.db.ReferenceProperty'> types.
Code:
user = self.auth.store.user_model.create_user(email,
password_raw=newpasswd)
if not user[0]: # user is a tuple
return user[1] # Error message
else:
# User is created, let's try redirecting to login page
okuser = auth_models.User.get_by_id(long(user[1].key.id()))
okuser.sponsor = db.SelfReferenceProperty(auth_models.User.get_by_id(sponsor_id),collection_name='matched_distributor')
okuser.put()
It forces me do use a string instead of a reference and then a solution is feasible:
user = self.auth.store.user_model.create_user(email,
password_raw=newpasswd)
if not user[0]: # user is a tuple
return user[1] # Error message
else:
# User is created, let's try redirecting to login page
okuser = auth_models.User.get_by_id(long(user[1].key.id()))
okuser.sponsor = sponsor_id
okuser.put()
You can't assign an instance of a Property class to an instance of a model - property classes define properties, they don't represent individual values.
By far the easiest way to do what you want is to add the property as you would on a regular model. Just because you're using expandos (why, by the way?) doesn't mean you can't have regular properties on them as well.

Google App Engine - Is this also a Put method? Or something else

Was wondering if I'm unconsciously using the Put method in my last line of code ( Please have a look). Thanks.
class User(db.Model):
name = db.StringProperty()
total_points = db.IntegerProperty()
points_activity_1 = db.IntegerProperty(default=100)
points_activity_2 = db.IntegerProperty(default=200)
def calculate_total_points(self):
self.total_points = self.points_activity_1 + self.points_activity_2
#initialize a user ( this is obviously a Put method )
User(key_name="key1",name="person1").put()
#get user by keyname
user = User.get_by_key_name("key1")
# QUESTION: is this also a Put method? It worked and updated my user entity's total points.
User.calculate_total_points(user)
While that method will certainly update the copy of the object that is in-memory, I do not see any reason to believe that the change will be persisted to the the datastore. Datastore write operations are costly, so they are not going to happen implicitly.
After running this code, use the datastore viewer to look at the copy of the object in the datastore. I think that you may find that it does not have the changed total_point value.

Resources