
Research
SANDWORM_MODE: Shai-Hulud-Style npm Worm Hijacks CI Workflows and Poisons AI Toolchains
An emerging npm supply chain attack that infects repos, steals CI secrets, and targets developer AI toolchains for further compromise.
gando
Advanced tools
A framework based on Django that has tried to gather together the tools needed in the process of creating a large project.
Gando is a collection of batteries-included tools, conventions and scaffolding utilities built on top of Django to standardize and accelerate development for the Horin engineering teams. Named after Gando — a small, native crocodile of Sistan and Baluchestan — the project is compact, tough and purpose-built for the local needs of your org.
startmodel, startapi, startservice, startinterface, ...), and utilities (image
converters/uploaders, string casings, etc).AbstractBaseModel, WhiteAbstractBaseModel, AbstractBaseModelFaster (history-enabled,
timestamps, availability flag).AvailableManager (named Manager in current code) that filters available=1 by default.ImageField (multi-subfields), ImageProperty descriptor, BlurBase64Field computed preview.PhoneNumberField, UsernameField, PasswordField, BooleanNumberField.BaseModelAdmin — unified Admin list/filters/readonly behavior and image field rendering.ResponseSchema, RequestSchema, Base / BaseInterface for method dispatch and unified response
format.# (recommended: inside virtualenv)
pip install gando
or for local editable development:
git clone https://github.com/navidsoleymani/gando.git
cd gando
pip install -e .
Minimum safe setup.py/pyproject expectations:
python_requires='>=3.8'Django>=4.2,<5.0, djangorestframework>=3.12.INSTALLED_APPS in settings.py:INSTALLED_APPS = [
# ...
"gando",
# other apps
]
from gando.models import AbstractBaseModel # path may vary
class Article(AbstractBaseModel):
title = models.CharField(max_length=300)
body = models.TextField()
from gando.models import AbstractBaseModel, ImageField
class Product(AbstractBaseModel):
image = ImageField()
title = models.CharField(max_length=255)
from django.contrib import admin
from gando.admin import BaseModelAdmin
from .models import Product
@admin.register(Product)
class ProductAdmin(BaseModelAdmin):
list_display = ['title', 'image']
python manage.py startmodel -al your_app_label -mn Product
python manage.py startapi -al your_app_label -an Product
python manage.py startservice -al your_app_label -sn Product
python manage.py startinterface -al your_app_label -in Product
(The flags are: -al/--applabel, -mn/--modelname, -an/--apiname, -sn/--servicename, -in/--interfacename.)
python manage.py makemigrations
python manage.py migrate
python manage.py runserver
Design philosophies (applies across gando):
architectures contains apis, interfaces, services, models and serializers and
enforces a flow: API / Interface -> Service -> Repo/Models -> Serializers -> Response.RequestSchema / ResponseSchema shapes to avoid divergence.repo, schemas, apis,
services).Typical request flow:
RequestSchema.BaseInterface or Base dispatches to a method (get, post, etc).Service that encapsulates business logic.Service interacts with repo models and returns domain data.ResponseSchema and returned.Model:
from gando.models import AbstractBaseModel, ImageField
class Banner(AbstractBaseModel):
title = models.CharField(max_length=200)
image = ImageField()
Service (conceptual):
from gando.architectures.services import BaseService
class BannerService(BaseService):
def get_banner(self, banner_id):
banner = Banner.objects.filter(id=banner_id).first()
if not banner:
return {"error": "not_found"}, 404
return {"banner": BannerSchema.from_orm(banner).dict()}, 200
API / Interface (conceptual):
from gando.architectures.apis import BaseAPI # or BaseInterface
class BannerAPI(BaseAPI):
def get(self, request, banner_id):
data, status = BannerService().get_banner(banner_id)
return ResponseSchema(success=(status == 200), status_code=status, data=data)
Client-side Response wrapper — BaseResponseSchema expects the JSON shape used across gando.
Gando provides a set of management commands that scaffold folders and files in the target app:
startmodel -al <app_label> -mn <modelname>
Creates repo/models/__<ModelName>.py, updates repo/models/__init__.py, app-level models.py, admin.py entries,
repo/schemas/models/__<modelname>.py, and URL includes.
startapi -al <app_label> -an <apiname>
Creates repo/apis/__<ApiName>.py with a base API class.
startservice -al <app_label> -sn <servicename>
Creates service module templates and schema folders.
startinterface -al <app_label> -in <interfacename>
Creates interface templates under repo/interfaces.
Note: commands rely on settings.BASE_DIR and the app folder structure; run them from the project root.
Gando standardizes API payloads. A canonical successful response includes:
{
"success": true,
"status_code": 200,
"has_warning": false,
"exception_status": false,
"monitor": {},
"messenger": [],
"many": false,
"data": {
/* object or list depending on many */
},
"development_messages": {}
}
ResponseSchema (server) and BaseResponseSchema (client helpers) map to the same shape. Use these everywhere to keep
client & server consistent, reduce parsing errors and simplify error handling.
I reviewed the code you supplied deeply. Below are concrete findings and recommendations to make Gando safer, more robust and production-friendly.
Manager naming & available fieldManager. It's better to name AvailableManager or ActiveManager.BooleanField if available is binary; if you anticipate more states, keep
integer but rename to status/availability_state with an Enum.QueryDictSerializer problems__image_field_name_parser uses a equal variable that is not correctly reset per loop — this creates incorrect
prefix matching.__updater function is fragile and can lose values or produce inconsistent structures for nested merges.__media_url uses a bare except: — this hides unrelated errors.Fix suggestions (high level):
str.startswith(prefix).deep_merge(a, b) for dictionaries where b values override or are appended appropriately.getattr(settings, "MEDIA_URL", "") instead of try/except.Short fixed sketch (extract):
# safer prefix check
def __image_field_name_parser(self, field_name):
for img in self.image_fields_name:
if field_name.startswith(img + "_"):
return [img, field_name[len(img) + 1:]]
return [field_name]
# safer media url
from django.conf import settings
@property
def __media_url(self):
return getattr(settings, 'MEDIA_URL', '')
(I can provide a full refactor patch if you want — it will be longer but makes the serializer robust.)
BlurBase64Field.pre_save and remote storagesmall_blur_base64(_src.file.name) assumes a local filename and direct filesystem access. If you use
remote storages (S3, GCS) this will fail.from django.core.files.storage import default_storage
if _src:
try:
with default_storage.open(_src.name, 'rb') as fh:
blur = small_blur_base64(fh.read()) # accept bytes in small_blur_base64
setattr(model_instance, self.attname, blur)
except Exception:
# handle/log but don't silence unexpected exceptions
raise
Also consider moving small_blur_base64 processing off the request thread to a background job (Celery) or compute it
once on upload.
except: usageexcept: usages that silence all exceptions. Replace with targeted exception types, or at least log
and re-raise unexpected ones.BaseModelAdmin behavior & nameslist_display setter uses id_, available_ names which are fine, but keep docstrings and explicit list_display
examples in README.image_fields_name_list usage in admin to ensure image fieldsets are created.kwargs dict and set self.app_label = kwargs. The setter then reads
kwargs.get('applabel'). This works but is unusual — be explicit in docs that flags are named -al/--applabel etc.
Also check behavior when flags are missing: the code raises CommandError as expected.HistoricalRecords on many models can balloon DB size. Add guidance for history pruning or retention policies.setup.py you currently list unpinned dependencies: prefer minimum versions and ranges for stability, e.g.
Django>=4.2,<5.0, djangorestframework>=3.12,<4.0.Recommended development stack for the repo:
sphinx-autodoc + READTHEDOCS pipelineExample .github/workflows/ci.yml stages:
black --check, isort --check, flake8.pytest.Use Semantic Versioning (MAJOR.MINOR.PATCH) and maintain a CHANGELOG.md following Keep a Changelog.
black and check flake8.Please include unit tests for behavior changes and update docs when public APIs change.
Gando is released under the MIT License. See LICENSE for details.
Author: Hydra (navidsoleymani@ymail.com) — repository:
https://github.com/navidsoleymani/gando.git.
1) Safer __media_url:
from django.conf import settings
@property
def __media_url(self):
return getattr(settings, 'MEDIA_URL', '')
2) Simpler image prefix parser:
def __image_field_name_parser(self, field_name):
# return [prefix, suffix] if field_name starts with any image prefix
for img in self.image_fields_name:
prefix = f'{img}_'
if field_name.startswith(prefix):
return [img, field_name[len(prefix):]]
return [field_name]
3) Example deep_merge (dict merge):
def deep_merge(a, b):
if not isinstance(a, dict) or not isinstance(b, dict):
return b
out = dict(a)
for k, v in b.items():
if k in out and isinstance(out[k], dict) and isinstance(v, dict):
out[k] = deep_merge(out[k], v)
elif k in out and isinstance(out[k], list) and isinstance(v, list):
out[k] = out[k] + v
else:
out[k] = v
return out
You already have a strong, well-structured foundation. With a few fixes (robust serializer merging, safe storage access, explicit exception handling) and solid test coverage, Gando will be a great, reliable toolkit for Horin projects.
If you want, I can now:
README.md file ready to replace the one in your repo (I can drop it into the canmore
canvas or paste it here), orQueryDictSerializer, BlurBase64Field.pre_save, and
other issues I flagged.Which of those do you want me to do next?
FAQs
A framework based on Django that has tried to gather together the tools needed in the process of creating a large project.
We found that gando demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
An emerging npm supply chain attack that infects repos, steals CI secrets, and targets developer AI toolchains for further compromise.

Company News
Socket is proud to join the OpenJS Foundation as a Silver Member, deepening our commitment to the long-term health and security of the JavaScript ecosystem.

Security News
npm now links to Socket's security analysis on every package page. Here's what you'll find when you click through.