South Migration App Has No Migrations

If you get this error for an app that truly doesn’t have any migrations, and perhaps isn’t even related to your project, it most likely means the south_migrationhistory table has row that matches one of your INSTALLED_APPS.

This can happen if at one point the app had migrations, but the latest version doesn’t (happens to third party apps), or you once built an app with migrations in the early days of your project.

Remove it via SQL to fix the problem.

DELETE FROM south_migrationhistory WHERE app_name = ‘offending_appname’;

SQLAlchemy Order By Foreign Key Table Column

To order by a foreign key value, make sure you do a proper `.join` of the table you want to reference in addition to  `.options` and `joinedload` or `joinedload_all`, which is purely to retrieve data from each related row.

SQLAlchemy never ceases to amaze.. I’m glad I made the switch, as it is making me a better coder by not shielding me from the intricacies  of SQL.

http://docs.sqlalchemy.org/en/rel_0_7/orm/loading.html

How joinedload() in particular achieves this result of not impacting entity rows returned in any way is that it creates an anonymous alias of the joins it adds to your query, so that they can’t be referenced by other parts of the query. For example, the query below uses joinedload() to create a LEFT OUTER JOIN from users to addresses, however the ORDER BY added against Address.email_address is not valid – the Address entity is not named in the query:

>>> jack = session.query(User).\
... options(joinedload(User.addresses)).\
... filter(User.name=='jack').\
... order_by(Address.email_address).all()
SELECT addresses_1.id AS addresses_1_id, addresses_1.email_address AS addresses_1_email_address, addresses_1.user_id AS addresses_1_user_id, users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password FROM users LEFT OUTER JOIN addresses AS addresses_1 ON users.id = addresses_1.user_id WHERE users.name = ? ORDER BY addresses.email_address <– this part is wrong ! [‘jack’]

Above, ORDER BY addresses.email_address is not valid since addresses is not in the FROM list. The correct way to load the User records and order by email address is to use Query.join():

>>> jack = session.query(User).\
... join(User.addresses).\
... filter(User.name=='jack').\
... order_by(Address.email_address).all()
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password FROM users JOIN addresses ON users.id = addresses.user_id WHERE users.name = ? ORDER BY addresses.email_address [‘jack’]

Flask Cache Buster based on Git Revision

This is cool.

Here’s a simple flask cache buster that appends a version number to all static assets based on the current git revision short hash.

https://gist.github.com/yuchant/9108622

Flask is built so amazingly. It has hooks to allow modification of all urls generated by the system.


app = make_app()

def get_git_revision_hash():
'''
Get current revision short hash and use as cache buster key.
'''
import subprocess
return subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD'])

git_revision_hash = get_git_revision_hash()
STATIC_ENDPOINTS = [</div><div class="" id="file-gistfile1-txt-LC14">    'static',</div>

<div class="" id="file-gistfile1-txt-LC15">    'shop.static',</div><div class="" id="file-gistfile1-txt-LC16">]
@app.url_defaults
def static_cache_buster(endpoint, values):
if endpoint in STATIC_ENDPOINTS:
values['_v'] = git_revision_hash

Update: actually, git revision number is not what I’m looking for

This only works for developer driven content. It wouldn’t work for user uploads of course, therefore I modified this to read mtimes.

        STATIC_ENDPOINTS = [
            'static',
            'shop.static',
        ]
        @self.app.url_defaults
        def static_cache_buster(endpoint, values):
            if endpoint in STATIC_ENDPOINTS:
                # append timestamps of files
                modules = endpoint.split('.')
                if len(modules) == 2:
                    blueprint, _ = modules
                    static_folder = self.app.blueprints[blueprint].static_folder
                else:
                    static_folder = self.app.static_folder
                file_path = os.path.join(static_folder, values['filename'])
                mtime = os.path.getmtime(file_path)
                values['_v'] = mtime

Python Path the Easy Way. Add .pth Python Configuration Files

So python path is always a little daunting when coming to deployment. Especially since it’s typically done outside of your neatly wrapped software package.

The easiest way to do this so far is using python configuration files. Drop dead easy: put a .pth extension file in your site-packages directory, where each line corresponds to an entry you want put into sys.path.

http://docs.python.org/2/install/#modifying-python-s-search-path

Loading Shopify Posts via Flask on demand. Flask is Amazing.

Flask is really impressing me. It lets me write very clean obvious code.

We are launching a new site, and need a blog ASAP. I figure it would be a pain to migrate blog hosts so I was curious what I can do with Shopify’s blog API.

I was able to..

  • Loop through Shopify’s API until we get all of the blog posts. This takes a long time.
  • Strip the funky HTML
  • Parse HTML / find first image
  • Cache the results for 30 minutes
  • Split into pages of 25 / separate by pages

Some of the recommended caching plugins are just so useful right out of the box. cache.memoize will cache the results of a function for timeout building a cache key out of the function arguments.

cache.cached caches the result of a view. That means I can really easily and fluidly adjust caches as necessary to keep performance high, even though we’re making many requests to Shopify to get the posts themselves.

Check out the gist
https://gist.github.com/yuchant/312d42568192a2bf3acc

I’m impressed.

How to add ordering to a custom module list with LemonStand.

It took me a long time to figure out how to add sorting to a LemonStand list view.

Honestly, adding a simple M2M relationship in LS took me many many hours and modification of 4 partials. It takes me about 30 seconds with Django. The documentation is hard to read (but a part of the problem is just low volume / lack of search results) and mostly you just have to read the source.

Working with LS or Magento gives me a newfound appreciation for my frameworks of choice: Django or Flask + SQLAlchemy. It’s truly like magic in comparison. To be fair, those are tool for building web apps and nothing comes “in the box”. It’s just what I prefer.

Anyways, please forgive the quality of the post; I would have killed for somebody to write even half of the below. Hope it helps you guys!

Mostly we are going to copy functionality from the “manage top products”. I am not even going to bother with renaming things. Surprisingly, no new assets are required, and there is no hacking of core LS required.

Follow along.

Copy from the shop_categories.php controller

Copy the “top products” action code from the Category controller into your own controller, copying templates and modifying code as needed.

$implement

Make sure you are $implementing (subclassing)

public $implement = 'Db_ListBehavior, Db_FilterBehavior, Db_FormBehavior';

Take the relevant pieces from the __construct function in shop_categories.php

if (Phpr::$router->action == 'manage_top_products')
{
	$this->list_no_pagination = true;
	$this->list_render_as_tree = false;
	$this->list_render_as_sliding_list = false;
	$this->list_model_class = 'Shop_Product';

	$this->list_custom_body_cells = PATH_APP.'/modules/shop/controllers/shop_categories/_top_product_body_cells.htm';
	$this->list_custom_head_cells = PATH_APP.'/modules/shop/controllers/shop_categories/_top_product_head_cells.htm';
	$this->list_custom_prepare_func = 'prepare_top_product_list';
	$this->list_record_url = null;
	$this->list_search_enabled = false;
	$this->list_no_setup_link = false;
	$this->list_no_setup_link = false;
	$this->list_no_data_message = 'There are no top products in this category';
	$this->list_name = 'top_products_list';
	$this->list_no_sorting = true;
}

I ended up copying these, but it turns out you don’t need to modify anything so it’s not a strict requirement.

Remove list_record_url / add your own, to make sure edit links work.

– ‘/modules/shop/controllers/shop_categories/_top_product_body_cells.htm’;
– ‘/modules/shop/controllers/shop_categories/_top_product_head_cells.htm’;

Override sorting

The page has no default sorting, so nothing will appear to work without this sort override. Paste this function into your controller, and supply the appropriate sort field (sort_field)

	public function listOverrideSortingColumn($sorting_column)
	{
		if (Phpr::$router->action == 'reorder_categories')
		{
			$result = array('field'=>'front_end_sort_order', 'direction'=>'asc');
			return (object)$result;
		}

		return $sorting_column;
	}

The AJAX set order function

Modify the class Shop_Category with your class. Make sure your class $implements Db_Sortable to get access to the set_orders function.

	protected function reorder_categories_onSetOrders()
	{
		try
		{
			Shop_Category::set_orders(post('item_ids'), post('sort_orders'));
		}
		catch (Exception $ex)
		{
			Phpr::$response->ajaxReportException($ex, true, true);
		}
	}

The Sort field getter function

Copy the function that takes an ID and returns the sort field. This was done because the sort field is not even on the product model directly.

Note that since I only have access to an ID, I am doing a very inefficient one query per sort field lookup. Oh well.

	protected function get_top_product_sort_order($id)
	{
		// return sort order for our model, given its ID.
		$ambassador = new Raen_Ambassador();
		$ambassador_sort_order = $ambassador->find_by_id($id)->sort_order;
		return $ambassador_sort_order;
	}

Copy sorting javascript

Copy the sorting javascript header block from manage_top_products.htm

In my case, I injected the followingn block into my module index.html file.

<? Phpr_View::beginBlock("head") ?>
	<script type="text/javascript" src="modules/shop/resources/javascript/top_category_products.js"></script>
	<link rel="stylesheet" href="modules/shop/resources/css/shop.css" type="text/css"/>
<? Phpr_View::endBlock() ?>

Modify your Models

Add sort field

Make sure your model has a sort_order field. It must be named sort_order, as the DB_Sortable has hard coded values.

Add Db_Sortable to your $implement.

Add after_create default sort order

Make sure a default sort order is applied to new elements, or sorting likes to blow up.

	public static function create()
	{
		return new self();
	}

	public function after_create()
	{
		Db_DbHelper::query("UPDATE {$this->table_name} set sort_order=:sort_order where id=:id", array(
			'sort_order'=>$this->id,
			'id'=>$this->id
		));
	}

Done!

Surprisingly, this works now w/o any modification to method names 🙂

Completed controller for reference: https://gist.github.com/yuchant/8683096
Completed model for reference: https://gist.github.com/yuchant/8683106 (sans fields)