{"version":"https:\/\/jsonfeed.org\/version\/1","title":"Software Crafts","home_page_url":"https:\/\/softwarecrafts.co.uk","feed_url":"https:\/\/softwarecrafts.co.uk\/rss\/100-words\/feed.json","description":"Short & snappy posts to build my writing habit","icon":"https:\/\/softwarecrafts.co.uk\/favicon.ico","author":{"name":"Software Crafts"},"items":[{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-303","content_html":"<p>I have had this idea\/theory for a while, that most software tools would benefit from a simple built in todo list built into the product directly. The main idea is for users to create jobs themselves or for the system itself to create jobs for the user to complete. The general thesis for this idea comes from a direct reference to the term &quot;Jobs to be done&quot;. Any piece of software that gets used, exists to complete a job and do it better than the solution before it.<\/p>\n<p>Well over the last couple of months I have turned this idea into a reality inside Hamilton Rock, or at least the first version of it and so far it seems very promising. The general API design is a few custom signals, a model and some signal reciever functions for the custom signals. The rest of the project interacts solely through the custom signals <code>job_requested<\/code> and <code>job_completed<\/code>. Each does as you would expect, <code>job_requested<\/code> requests a job be created, <code>job_completed<\/code> complete the related job to the model instance given. The core of the <code>job<\/code> model has a status, type and a generic foreign key which forms the target related to the job. Jobs can auto-completed when a condition has been met, it&#x27;s just a matter of firing off the completed signal. Example stub of the API is below.<\/p>\n<pre class=\"language-py\"><code class=\"language-py\">job_requested <span class=\"token operator\">=<\/span> Signal<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\njob_completed <span class=\"token operator\">=<\/span> Signal<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n\n<span class=\"token keyword\">class<\/span> <span class=\"token class-name\">Job<\/span><span class=\"token punctuation\">(<\/span>models<span class=\"token punctuation\">.<\/span>Model<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    content_type <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>ForeignKey<span class=\"token punctuation\">(<\/span>\n        ContentType<span class=\"token punctuation\">,<\/span>\n        on_delete<span class=\"token operator\">=<\/span>models<span class=\"token punctuation\">.<\/span>CASCADE<span class=\"token punctuation\">,<\/span>\n        help_text<span class=\"token operator\">=<\/span>_<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;The type of object this job is related to&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token punctuation\">)<\/span>\n    object_id <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>PositiveIntegerField<span class=\"token punctuation\">(<\/span>\n        help_text<span class=\"token operator\">=<\/span>_<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;The ID of the related object&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token punctuation\">)<\/span>\n    target <span class=\"token operator\">=<\/span> GenericForeignKey<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;content_type&quot;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token string\">&quot;object_id&quot;<\/span><span class=\"token punctuation\">)<\/span>\n    \n    assigned_to <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>ForeignKey<span class=\"token punctuation\">(<\/span>\n        settings<span class=\"token punctuation\">.<\/span>AUTH_USER_MODEL<span class=\"token punctuation\">,<\/span>\n        on_delete<span class=\"token operator\">=<\/span>models<span class=\"token punctuation\">.<\/span>SET_NULL<span class=\"token punctuation\">,<\/span>\n        null<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">,<\/span>\n        blank<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">,<\/span>\n        related_name<span class=\"token operator\">=<\/span><span class=\"token string\">&quot;assigned_jobs&quot;<\/span><span class=\"token punctuation\">,<\/span>\n        help_text<span class=\"token operator\">=<\/span>_<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;User this job is assigned to (default notification recipient)&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token punctuation\">)<\/span>\n    \n    job_type <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>CharField<span class=\"token punctuation\">(<\/span>\n        max_length<span class=\"token operator\">=<\/span><span class=\"token number\">50<\/span><span class=\"token punctuation\">,<\/span>\n        choices<span class=\"token operator\">=<\/span>jobTypeChoices<span class=\"token punctuation\">.<\/span>choices<span class=\"token punctuation\">,<\/span>\n        help_text<span class=\"token operator\">=<\/span>_<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;The type of job&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token punctuation\">)<\/span>\n    \n    status <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>CharField<span class=\"token punctuation\">(<\/span>\n        max_length<span class=\"token operator\">=<\/span><span class=\"token number\">20<\/span><span class=\"token punctuation\">,<\/span>\n        choices<span class=\"token operator\">=<\/span>StatusChoices<span class=\"token punctuation\">.<\/span>choices<span class=\"token punctuation\">,<\/span>\n        default<span class=\"token operator\">=<\/span>StatusChoices<span class=\"token punctuation\">.<\/span>PENDING<span class=\"token punctuation\">,<\/span>\n        db_index<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">,<\/span>\n        help_text<span class=\"token operator\">=<\/span>_<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;Current status of the job&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token punctuation\">)<\/span>\n    created_at <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>DateTimeField<span class=\"token punctuation\">(<\/span>\n        null<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">,<\/span>\n        blank<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">,<\/span>\n        help_text<span class=\"token operator\">=<\/span>_<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;When the job was completed or cancelled&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token punctuation\">)<\/span> \n    updated_at <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>DateTimeField<span class=\"token punctuation\">(<\/span>\n        null<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">,<\/span>\n        blank<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">,<\/span>\n        help_text<span class=\"token operator\">=<\/span>_<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;When the job was completed or cancelled&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token punctuation\">)<\/span> \n    completed_at <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>DateTimeField<span class=\"token punctuation\">(<\/span>\n        null<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">,<\/span>\n        blank<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">,<\/span>\n        help_text<span class=\"token operator\">=<\/span>_<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;When the job was completed or cancelled&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token punctuation\">)<\/span>\n    expires_at <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>DateTimeField<span class=\"token punctuation\">(<\/span>\n        null<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">,<\/span>\n        blank<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">,<\/span>\n        db_index<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">,<\/span>\n        help_text<span class=\"token operator\">=<\/span>_<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;When this job should be auto-cancelled if still pending&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token punctuation\">)<\/span>\n    metadata <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>JSONField<span class=\"token punctuation\">(<\/span>\n        default<span class=\"token operator\">=<\/span><span class=\"token builtin\">dict<\/span><span class=\"token punctuation\">,<\/span>\n        blank<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">,<\/span>\n        help_text<span class=\"token operator\">=<\/span>_<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;Additional job-specific data&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token punctuation\">)<\/span>\n    \n<span class=\"token decorator annotation punctuation\">@receiver<\/span><span class=\"token punctuation\">(<\/span>job_requested<span class=\"token punctuation\">)<\/span>\n<span class=\"token keyword\">def<\/span> <span class=\"token function\">handle_job_requested<\/span><span class=\"token punctuation\">(<\/span>  <span class=\"token comment\"># NOQA: PLR0913<\/span>\n    sender<span class=\"token punctuation\">,<\/span>\n    target<span class=\"token punctuation\">,<\/span>\n    job_type<span class=\"token punctuation\">,<\/span>\n    metadata<span class=\"token operator\">=<\/span><span class=\"token boolean\">None<\/span><span class=\"token punctuation\">,<\/span>\n    idempotent<span class=\"token operator\">=<\/span><span class=\"token boolean\">False<\/span><span class=\"token punctuation\">,<\/span>\n    assigned_to<span class=\"token operator\">=<\/span><span class=\"token boolean\">None<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token operator\">**<\/span>kwargs<span class=\"token punctuation\">,<\/span>\n<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    <span class=\"token comment\"># Creates a Job<\/span>\n    <span class=\"token keyword\">pass<\/span>\n    \n<span class=\"token decorator annotation punctuation\">@receiver<\/span><span class=\"token punctuation\">(<\/span>task_completed<span class=\"token punctuation\">)<\/span>\n<span class=\"token keyword\">def<\/span> <span class=\"token function\">handle_task_completed<\/span><span class=\"token punctuation\">(<\/span>sender<span class=\"token punctuation\">,<\/span> target<span class=\"token punctuation\">,<\/span> task_type<span class=\"token punctuation\">,<\/span> metadata<span class=\"token operator\">=<\/span><span class=\"token boolean\">None<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token operator\">**<\/span>kwargs<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    <span class=\"token comment\"># Completes a Job<\/span>\n    <span class=\"token keyword\">pass<\/span>\n<\/code><\/pre>\n<p>Currently all jobs are created by the system and we have yet to directly expose them to our users, however there have been two interesting insights from where I have used them to date (onboarding &amp; staff pages). First in onboarding it has created a flexible way to add\/remove steps for me as a developer and given that completing a job is part of the internal state rather than just a custom form or series of forms, the logic to visualise where users are stalling in our flow becomes easy to visualise. It&#x27;s how many pending jobs at each step and how long have they been pending?<\/p>\n<p>Second when an handled exception case occurs in the application which requires the attention of a staff user, instead of a log statement needing to be processed by some system and being perhaps overfilled with extra context, or there being extra noise in a developer tool such as Sentry, we simply create a job of the type we need. Then create a staff page to list and handle jobs of this type.<\/p>\n<p>Finally this jobs architecture has enabled a central point to control how we send notifications (emails etc) from the system. Every notification is linked to a Job, which then means it forces notifications to not deal with too many actions at a time and again allows a natural tracking of which notifications have been actioned or not.<\/p>\n<p>In future, I&#x27;m likely to expose these jobs to our users more directly, giving them a native solution to spend just the right amount of time in our product and no more, so what we build is simple &amp; useful.<\/p>\n<p>What do you think? Would you like this as a package for you to play with? Let me know and I may just break it out and release it!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-303","title":"Representing 'Jobs to be Done' in a project","summary":"An experiment in building out a new architecture for projects","date_modified":"2026-04-14T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#jobsToBeDone","#systems_architecture"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-302","content_html":"<p>I have recently been reading Swizec Teller&#x27;s new book <a href=\"https:\/\/www.scalingfastbook.com\">Scaling Fast<\/a> and in it he mentions architectural complexity, which reminded me of my desire for a tool that combines database dependencies between Django apps and import dependencies between Django apps. To date, I have used other tools such as graph models from Django extensions, <a href=\"https:\/\/import-linter.readthedocs.io\/en\/stable\/\">import-linter<\/a> is the most recent one, and <code>pyreverse<\/code> from Pylint. They all do bits of the job, but require manual stitching together to get a cohesive graph of everything overlaid in the right way. So I remembered about this, and so over the last couple of days, I&#x27;ve built a new package which combines all of this into a live view which updates as you build your app, a management command and a panel for Debug Toolbar.<\/p>\n<p>Why the Django app level, you ask? Primarily, I do find models good, but they can get a little too complicated and a little you get a few too many lines and doing imports at the module level within an app or like separating it all out, again, you lose it becomes there becomes too much noise to signal to really understand the logical relationship between different components in the system. I like to think that Django apps naturally represent logical representations of different parts of a project or a system. A project obviously is too large unless you&#x27;re dealing with multiple projects, but within a single Django project, it&#x27;s a good representation to have an app deal with one thing. You can I know you can structure Django projects &amp; apps in many ways. So it&#x27;d be interesting to see this tool used on other&#x27;s project structures that aren&#x27;t one app for a single logical component.<\/p>\n<p>So without further ado, here is <a href=\"https:\/\/github.com\/softwarecrafts\/django-dependency-map?tab=readme-ov-file\">Django Dependency Map<\/a>, which combines output from Django extensions <code>graph_models<\/code> and  grimp, which is used by <code>import-linter<\/code> to dynamically map the dependencies between your different apps and third-party apps. Initially, it was a management command, which then outputs a HTML file, which exists. I then added that into a live view, and there&#x27;s an integration into Django debug toolbar.<\/p>\n<p>The live map page has the following features:<\/p>\n<ul>\n<li>you can hide nodes and kind of see how the dependencies change.<\/li>\n<li>force graph &amp; hierarchical graph representation,<\/li>\n<li>Detailed information on a single app and its relationships<\/li>\n<li>import cycle detection<\/li>\n<li>import violations from import-linter<\/li>\n<li>Debug toolbar panel<\/li>\n<li>Export of the graph to mermaid &amp; dot formats<\/li>\n<\/ul>\n<p>My hope is twofold. One, it might reveal things about your projects that you didn&#x27;t know about in terms of how fit how interlinked things are. And secondly, I hope it may change the way you build your Django apps. I&#x27;m hoping to have it open as another tab and just to watch as I&#x27;m building things to make sure out as I&#x27;m and maybe as an agent&#x27;s building things see use it as a sense check of if it&#x27;s doing things right or as I expect it to in terms of overall architecture rather than at the code level.<\/p>\n<p>The pypi package is coming very soon, but you can visit the repo here: <a href=\"https:\/\/github.com\/softwarecrafts\/django-dependency-map\">https:\/\/github.com\/softwarecrafts\/django-dependency-map<\/a><\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-302","title":"New Package: Django Dependency Map","summary":"Understanding the interaction between Django apps as you build","date_modified":"2026-04-08T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#packages","#dependencies","#architecture"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-301","content_html":"<p>The past few weeks have been both amazing and frustrating. If you listen\/read to my weekly in\u2011progress reports have already I mentioned some of this. In the last week of February, I appeared on the <a href=\"https:\/\/djangochat.com\/episodes\/freelancing-community-andrew-miller-seDEJ66s\">Django Chat podcast<\/a>, which was released to positive feedback. I considered that a personal win for my career, it was fun to chat with Will and Carlton and hope to do more like this in future. However, two days after the episode aired, I recieved news that a client had passed away, causing that income source to vanish overnight. My condolences go to their family and I was also in shock, personally and then from the perspecive of my business.<\/p>\n<p>This was especially frustrating because I thought my cashflow for 2026 was solid, and I expected to be salaried before the end of year once Hamilton Rock secures funding, which we plan on doing this year. Generally from a business perspective, income can vanish overnight, and I accept this. This risk is inherent in running a business, unlike an employed job in the UK, which offers protections in this regard. The risk and potential downside has a counter of greater flexibility. I can work where and when I like, take long lunch breaks, and take holidays without needing permission\u2014aside from checking in with the wife!<\/p>\n<p>However if you lose a job, <strong>all<\/strong> your income can disappear at once, it&#x27;s the classic &quot;all your eggs in one basket&quot; scenario. With a business, there is the opportunity to diversify, so thankfully this lost income wasn&#x27;t everything for me, but it doesn significantly shortern my cashflow runway. My next steps are to explore several options: revisiting Comfort Monitor Live and improving its marketing with the help of Claude, and experimenting with agents to generate additional revenue. Finally there is of course new freelance contracts or projects.<\/p>\n<p>For now, that\u2019s where things stand with me, I&#x27;m available for freelance contracts or projects. If you need a reliable developer with Django expertise, feel free to get in touch. I am open to part-time or project\u2011based work. Whether you need something built or maintained that your team lacks the capacity for, or you want advice on AI, development, or anything in between, drop me an email and or <a href=\"https:\/\/meetwith.softwarecrafts.co.uk\/\">book a call<\/a>.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-301","title":"The highs and lows of running a business","summary":"...recent news from me and how I am dealing with it","date_modified":"2026-03-10T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#business","#freelancing","#jobs"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-300","content_html":"<p>At the end of January, I was building out the deployment and infrastructure components for the startup project, so figured it would be an appropriate time to document how I think about these concepts at high level, perhaps they will help others. Generally I think about these processes two ways. First, is to create an environment, such as a virtual machine, PaaS, or container with a code spaced hole in it for your application, then create a process that moves the code from source control into that code spaced hole environment. This represents the initial deployment at a high level. Second, I think of deployments as pipelines. With the rise of infrastructure as code over the past decade, traditional CI\/CD pipelines have become cyclical: code is pushed, deployed to production, and the cycle repeats. Infrastructure code is similar to application code, but its cadence is much slower. While a typical application deployment aims for multiple pushes per day\u2014or at least a few per week\u2014Infrastructure as Code (IaC) is usually deployed far less frequently, often annually. Early in a project, or when creating environments for feature branches, infrastructure deployments may occur more often, but they remain cyclical: a code push triggers an action that updates the infrastructure.<\/p>\n<p>Both application and infrastructure code require state management. Application code often involves database migrations, where the current state is known and migrations are applied directly. In contrast, infrastructure can drift over time, requiring tools to read the existing state and apply only the necessary changes. Managing this state is crucial; for example, you wouldn&#x27;t redeploy an entire domain each time\u2014some elements, like DNS records, must remain consistent to avoid breaking the system.<\/p>\n<p>I like to think of IaC as building with Legos: components such as networking, load balancers, instances, databases, and caches are assembled into an application, which is then placed into an environment like staging or production. Some resources, like DNS records or mail settings, exist outside these environments to keep them in a global environment and reduce blast radius if something fails. This separation ensures that a failure in one environment doesn&#x27;t affect an entire company. Finally, a bootstrap or management environment provides out\u2011of\u2011band control for emergency recovery, enforcing the principle of least privilege.<\/p>\n<p>This high\u2011level view covers the initial deployment cycle; ongoing operation, monitoring, and maintenance are separate concerns. Ideally, I would like to see IaC repos that could be treated like a pipeline, allowing continuous deployment despite the need to read existing state rather than simply overwriting it, but then I am not an expert into the internals of these systems and have no desire to be an expert at this stage in my career. However the above concepts allows me to from zero to deployed with Pulumi code (having never used it before) in a matter of days rather than weeks.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-300","title":"Deploying a project to the world","summary":"How I think about infrastructure and deployments","date_modified":"2026-02-18T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#infrastructure","#IaC","#deployments"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-299","content_html":"<p>Towards the end of last year I was working with form renderers in my startup to provide a consistent interface for forms across the project. I also used template partials to override widgets, delivering consistent rendering all in one file, a nice win. This made me wonder what other common components in a project get rendered to HTML that could benefit from a single, reusable place to avoid repeating myself.<\/p>\n<p>Carlton&#x27;s Neapolitan package already has this to some degree. There are two template tag types: one for object detail and one for object list. We also have FormRenderers in Django which already cascade from project down to an individual form, so perhaps we could apply the same logic to render models in a dry, configurable way rather than duplicating templates and logic. This made me wonder, could we have a python class whose role is to define how a model get&#x27;s rendered? Let&#x27;s be clear, we&#x27;re not getting into serializers here and the validation or logic that comes with them, it&#x27;s similar to the separation of Forms and FormRenders.<\/p>\n<p>I&#x27;m thinking that this idea allows the rendering of an object of list of objects in a template like so:<\/p>\n<pre class=\"language-django\"><code class=\"language-django\">\n<span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{{<\/span> <span class=\"token variable\">object<\/span> <span class=\"token delimiter punctuation\">}}<\/span><\/span>\n\n<span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{{<\/span> <span class=\"token variable\">object_list<\/span> <span class=\"token delimiter punctuation\">}}<\/span><\/span>\n<\/code><\/pre>\n<p>This can be controlled via a class described above:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\">\n<span class=\"token keyword\">class<\/span> <span class=\"token class-name\">MyModelRenderer<\/span><span class=\"token punctuation\">(<\/span>ModelRenderer<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    list_template <span class=\"token operator\">=<\/span> <span class=\"token string\">&#x27;&#x27;<\/span>\n    detail_template <span class=\"token operator\">=<\/span> <span class=\"token string\">&#x27;&#x27;<\/span>\n    form_renderer <span class=\"token operator\">=<\/span> MyFormRenderer\n<\/code><\/pre>\n<p>The above class controls how a list of objects would be rendered along with a single object and the form renderer to use. The form side opens up the idea of chaining renderers together in order to find the correct template to use. This then links to the idea of having a template snippet for rendering related models. If you have a foreign key or a many-to-many relationship, your renderer could specify how to render itself as a related field. You could chain model renderers together so that, when rendering a related field, it looks up the appropriate snippet instead of rendering the entire detail or the entire list.<\/p>\n<p>This obviously would be an optional API, but a potentially interesting one. It would certainly alter the look of a Django project and of course nothing stops you from rendering by hand. To me this leans into a different approach to having shared components at the template level, pushing towards not repeating yourself where possible.<\/p>\n<p>Does this peak your interest or does this scream of nothing like Django at all? Let me know your thoughts!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-299","title":"ModelRenderers, a possible new component to Django...","summary":"Extending the idea of FormRenderers to other parts of Django","date_modified":"2026-02-02T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#renderers","#templates","#models"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-298","content_html":"<p>As I have been building out the startup (launching very very soon), I was struck by the pattern that I mentioned last year of Claude grouping lots partials into a single file. I think we are almost at the point of being able to define simple components in a template without a templatetag. My goal here is discovering how much we can do with the vanilla Django Template language with the recent additions. I still need to work on the specifics, but currently I have a file called <code>icons.html<\/code> in my project which has the lots of the following:<\/p>\n<pre class=\"language-django\"><code class=\"language-django\"><span class=\"token django language-django\"><span class=\"token comment\">{# Heroicons (outline, 24px) as template partials #}<\/span><\/span>\n<span class=\"token django language-django\"><span class=\"token comment\">{# Usage: {% include &quot;icons\/heroicons.html#wallet&quot; %} #}<\/span><\/span>\n<span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{%<\/span> <span class=\"token tag keyword\">partialdef<\/span> <span class=\"token variable\">wallet<\/span> <span class=\"token delimiter punctuation\">%}<\/span><\/span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>svg<\/span> <span class=\"token attr-name\">class<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>size-5 text-gray-500 group-has-[:checked]:text-white<span class=\"token punctuation\">&quot;<\/span><\/span>\n     <span class=\"token attr-name\">viewBox<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>0 0 24 24<span class=\"token punctuation\">&quot;<\/span><\/span>\n     <span class=\"token attr-name\">fill<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>none<span class=\"token punctuation\">&quot;<\/span><\/span>\n     <span class=\"token attr-name\">stroke<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>currentColor<span class=\"token punctuation\">&quot;<\/span><\/span>\n     <span class=\"token attr-name\">stroke-width<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>1.5<span class=\"token punctuation\">&quot;<\/span><\/span>\n     <span class=\"token attr-name\">stroke-linecap<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>round<span class=\"token punctuation\">&quot;<\/span><\/span>\n     <span class=\"token attr-name\">stroke-linejoin<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>round<span class=\"token punctuation\">&quot;<\/span><\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>path<\/span> <span class=\"token attr-name\">d<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>M21 12C21 10.7574 19.9926 9.75 18.75 9.75H15C15 11.4069 13.6569 12.75 12 12.75C10.3431 12.75 9 11.4069 9 9.75H5.25C4.00736 9.75 3 10.7574 3 12M21 12V18C21 19.2426 19.9926 20.25 18.75 20.25H5.25C4.00736 20.25 3 19.2426 3 18V12M21 12V9M3 12V9M21 9C21 7.75736 19.9926 6.75 18.75 6.75H5.25C4.00736 6.75 3 7.75736 3 9M21 9V6C21 4.75736 19.9926 3.75 18.75 3.75H5.25C4.00736 3.75 3 4.75736 3 6V9<span class=\"token punctuation\">&quot;<\/span><\/span> <span class=\"token punctuation\">\/&gt;<\/span><\/span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>svg<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n<span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{%<\/span> <span class=\"token tag keyword\">endpartialdef<\/span> <span class=\"token variable\">wallet<\/span> <span class=\"token delimiter punctuation\">%}<\/span><\/span>\n<span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{%<\/span> <span class=\"token tag keyword\">partialdef<\/span> <span class=\"token variable\">percent<\/span><span class=\"token operator\">-<\/span><span class=\"token variable\">badge<\/span> <span class=\"token delimiter punctuation\">%}<\/span><\/span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>svg<\/span> <span class=\"token attr-name\">class<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>size-5 text-gray-500 group-has-[:checked]:text-white<span class=\"token punctuation\">&quot;<\/span><\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    ....\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>svg<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n<span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{%<\/span> <span class=\"token tag keyword\">endpartialdef<\/span> <span class=\"token variable\">percent<\/span><span class=\"token operator\">-<\/span><span class=\"token variable\">badge<\/span> <span class=\"token delimiter punctuation\">%}<\/span><\/span>\n\n<span class=\"token django language-django\"><span class=\"token comment\">{# etc.. #}<\/span><\/span>\n<\/code><\/pre>\n<p>The general problem here is quite a few things are hardcode so to have different coloured icons would be repeating each partial which is obviously not ideal. I have a proof of concept that would allow this kind of repeatablity. It looks like this:<\/p>\n<pre class=\"language-django\"><code class=\"language-django\"><span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{%<\/span> <span class=\"token tag keyword\">partialdef<\/span> <span class=\"token variable\">star<\/span><span class=\"token operator\">-<\/span><span class=\"token variable\">solid<\/span> <span class=\"token delimiter punctuation\">%}<\/span><\/span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>path<\/span> <span class=\"token attr-name\">fill-rule<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>evenodd<span class=\"token punctuation\">&quot;<\/span><\/span> <span class=\"token attr-name\">d<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>M10.788 3.21c.448-1.077 1.976-1.077 2.424 0l2.082 5.006 5.404.434c1.164.093 1.636 1.545.749 2.305l-4.117 3.527 1.257 5.273c.271 1.136-.964 2.033-1.96 1.425L12 18.354 7.373 21.18c-.996.608-2.231-.29-1.96-1.425l1.257-5.273-4.117-3.527c-.887-.76-.415-2.212.749-2.305l5.404-.434 2.082-5.005Z<span class=\"token punctuation\">&quot;<\/span><\/span> <span class=\"token attr-name\">clip-rule<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>evenodd<span class=\"token punctuation\">&quot;<\/span><\/span> <span class=\"token punctuation\">\/&gt;<\/span><\/span>\n<span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{%<\/span> <span class=\"token tag keyword\">endpartialdef<\/span> <span class=\"token variable\">star<\/span><span class=\"token operator\">-<\/span><span class=\"token variable\">solid<\/span> <span class=\"token delimiter punctuation\">%}<\/span><\/span>\n\n<span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{%<\/span> <span class=\"token tag keyword\">partialdef<\/span> <span class=\"token variable\">credit<\/span><span class=\"token operator\">-<\/span><span class=\"token variable\">card<\/span> <span class=\"token delimiter punctuation\">%}<\/span><\/span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>path<\/span> <span class=\"token attr-name\">stroke-linecap<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>round<span class=\"token punctuation\">&quot;<\/span><\/span> <span class=\"token attr-name\">stroke-linejoin<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>round<span class=\"token punctuation\">&quot;<\/span><\/span> <span class=\"token attr-name\">d<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>M2.25 8.25h19.5M2.25 9h19.5m-16.5 5.25h6m-6 2.25h3m-3.75 3h15a2.25 2.25 0 0 0 2.25-2.25V6.75A2.25 2.25 0 0 0 19.5 4.5h-15a2.25 2.25 0 0 0-2.25 2.25v10.5A2.25 2.25 0 0 0 4.5 19.5Z<span class=\"token punctuation\">&quot;<\/span><\/span> <span class=\"token punctuation\">\/&gt;<\/span><\/span>\n<span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{%<\/span> <span class=\"token tag keyword\">endpartialdef<\/span> <span class=\"token variable\">credit<\/span><span class=\"token operator\">-<\/span><span class=\"token variable\">card<\/span> <span class=\"token delimiter punctuation\">%}<\/span><\/span>\n\n<span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{%<\/span> <span class=\"token tag keyword\">partialdef<\/span> <span class=\"token variable\">icon<\/span> <span class=\"token delimiter punctuation\">%}<\/span><\/span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>svg<\/span> <span class=\"token attr-name\">class<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span><span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{{<\/span> <span class=\"token variable\">css_classes<\/span> <span class=\"token delimiter punctuation\">}}<\/span><\/span><span class=\"token punctuation\">&quot;<\/span><\/span> <span class=\"token attr-name\">fill<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>none<span class=\"token punctuation\">&quot;<\/span><\/span> <span class=\"token attr-name\">stroke-width<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>1.5<span class=\"token punctuation\">&quot;<\/span><\/span>\n<span class=\"token attr-name\">stroke<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>currentColor<span class=\"token punctuation\">&quot;<\/span><\/span> <span class=\"token attr-name\">viewBox<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>0 0 24 24<span class=\"token punctuation\">&quot;<\/span><\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n  <span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{%<\/span> <span class=\"token tag keyword\">with<\/span> <span class=\"token variable\">icon_path<\/span><span class=\"token operator\">=<\/span><span class=\"token string\">&quot;icons\/heroicons.html#&quot;<\/span><span class=\"token operator\">|<\/span><span class=\"token filter function\">add<\/span><span class=\"token punctuation\">:<\/span><span class=\"token variable\">icon_name<\/span> <span class=\"token delimiter punctuation\">%}<\/span><\/span>\n    <span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{%<\/span> <span class=\"token tag keyword\">include<\/span> <span class=\"token variable\">icon_path<\/span> <span class=\"token delimiter punctuation\">%}<\/span><\/span>\n  <span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{%<\/span> <span class=\"token tag keyword\">endwith<\/span> <span class=\"token delimiter punctuation\">%}<\/span><\/span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>svg<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n<span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{%<\/span> <span class=\"token tag keyword\">endpartialdef<\/span> <span class=\"token variable\">icon<\/span> <span class=\"token delimiter punctuation\">%}<\/span><\/span>\n<\/code><\/pre>\n<p>and is used like so:<\/p>\n<pre class=\"language-django\"><code class=\"language-django\"><span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{%<\/span> <span class=\"token tag keyword\">include<\/span> <span class=\"token string\">&quot;icons\/heroicons.html#icon&quot;<\/span> <span class=\"token keyword\">with<\/span> <span class=\"token variable\">icon_name<\/span><span class=\"token operator\">=<\/span><span class=\"token string\">&#x27;credit-cards&#x27;<\/span> <span class=\"token variable\">css_classes<\/span><span class=\"token operator\">=<\/span><span class=\"token string\">&#x27;size-10&#x27;<\/span> <span class=\"token delimiter punctuation\">%}<\/span><\/span>\n<\/code><\/pre>\n<p>There are a couple of issues that need to be figured out. First is the being allowed to specify different html attributes on the <code>svg<\/code> tag such as <code>fill<\/code>, <code>stroke<\/code>, <code>stroke-width<\/code> etc. I don&#x27;t have a solution, but I do know third-party libraries have template tags to handle this. Second is the hack to dynamically include a partial in the same file. Ideally the in the <code>icon<\/code> partial, we could specify the partial directly with a variable over using the with and include tags.<\/p>\n<p>I&#x27;m going to keep playing with this, but I like the appeal of having a single drop in template which then has a whole template pack available to a project. I could easily see a package which then has a file per icon pack (heroicons, font awesome, flaticon etc).<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-298","title":"Django Icon packs with template partials","summary":"More adventures and pushing the boundaries of vanilla Django","date_modified":"2026-01-20T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#partials","#templates"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-297","content_html":"<p>Following along the lines from <a href=\"\/100-words\/day-277\">one of posts<\/a> from last year about Django being a protocol or an API, there was a recent <a href=\"https:\/\/indiehackers.social\/deck\/@carlton@chaos.social\/115843976048274194\">discussion on Mastodon<\/a> about configurable content types, and there\u2019s a ticket in flight to implement this new feature that would make content types configurable. This made me think, in <a href=\"https:\/\/django-prodserver.readthedocs.io\/en\/latest\/\">django-prodserver<\/a>, I\u2019m already creating configurable backends, and in Django generally we have configurable components: databases, caches, storage backends, email backends, and so on. Most of whole system is configurable, and the <code>INSTALLED_APPS<\/code> setting could be considered the heart of this.<\/p>\n<p>I see some potential value in building out a meta design documents for how we expect new features to be built in Django, describing the kinds of designs or Python APIs we would anticipate in feature. For example, there\u2019s clearly a pattern of a configurable backend via settings, as I mentioned. But are there other conventions we should codify? Whether that takes the form of a contribution document, or an issue template in GitHub, but I think starting with some documentation would be a good start. It might also reveal some gaps in existing features that don&#x27;t meet the standard we would expect for new features being introduced.<\/p>\n<p>I\u2019m wondering if this is something people are interested in, or something that could smooth the adoption of a package into core though I won\u2019t claim it would speed things up, given how we pace ourselves. We do have established patterns; codifying them a bit more could help those who want to contribute back. Although this is a technical document, it serves more as a community document that shares our culture of how we build Django, therefore creating another on ramp for the community.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-297","title":"Documenting Django's technical culture","summary":"Documenting our design decisions for another on-ramp for new contributors","date_modified":"2026-01-08T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#contribution","#django","#design"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-296","content_html":"<p>As I prepared this post I realised I a lot more had happened this year than I thought, so without further a do here are the highlights I remember.<\/p>\n<p>The new year started strong with me achieving my goal of writing a blog post every week day for a year. Since then I have scaled it back to approxmiately weekly, but there have been longer gaps over holidays or when worked was a bit too busy. It was a definite bonus to get 9 of my articles featured in Django News this year! The other content production I have been doing this year is a short podcast called <a href=\"\/in-progress\">&quot;In Progress&quot;<\/a> which is a public-ish show about what I am working on week to week, I do plan to make this public at some point!<\/p>\n<p>My big focus this year has been building an initial build for a startup I am co-founding. It&#x27;s in the financial services space and I have a deadline for the end of January 2026. It&#x27;s taken much longer than I would have liked, partly due to the table stakes features that were required, partly due to only being able to work part-time on it for most the year. Context switching between this and other client work\/projects is a killer for productivity. However a win was moving to working mostly full-time from September and the pace certainly picked up then. The other contributing factor is LLM and agents finally clicking for me in my workflow. I don&#x27;t think I would be as far in the build if it hadn&#x27;t worked out for me.<\/p>\n<p>On the topic of AI, I see it as a useful tool that has enabled me to ship some ideas and products much quicker as a solo developer than previously. <a href=\"https:\/\/github.com\/nanorepublica\/django-deadcode\">django-deadcode<\/a> is an example of this. Additionally plenty of features in the startup and some client projects. My general view is that the human work has shifted left towards the product understanding and those that understand that along with technical knowledge are benefitting right now. That said I&#x27;m not sure the current technological approach is sustainable so I am personally ready to switch back to the &#x27;old&#x27; way or use a local model to continue using an LLM.<\/p>\n<p>Django continued to be a large focus for my free time this year. I attended DjangoCon Europe for the second time and it was great to meet the community especially after interacting with folks online for so long over the last couple of years, there was a lot of fun to be had and I plucked up the courage to give a lighting talk about my 100 words goal and my startup sponsored the conference which was cool. Django Social&#x27;s in Cambridge continued but at slightly slower pace mostly down to me forgetting to organise a date each month! In the online world, I now chair the Online Community Working Group and have become an admin on the Discord server. Finally I shipped a couple of packages, the previously mentioned django-deadcode was an AI experiment to see how much Claude could do before I even had to clone the repo locally (turns out a lot!). Second was finally pushing <a href=\"https:\/\/django-prodserver.readthedocs.io\/en\/latest\/\">django-prodserver<\/a> over the start line. I say start line as there is plenty of more work to do for this package and perhaps a merge into core Django?<\/p>\n<p>The final piece of work life is I shipped the latest feature of Comfort Monitor Live, yay! However lost the 2 paying customers I had. I would like to round off the product at some point (1 more feature left), but it&#x27;s currently on pause as my focus is on the items I have already mentioned in this post.<\/p>\n<p>Finally there was a good amount of personal highlights, we upgrade to an electric cargo bike this year which has been lovely and I got a new single speed after the one I built was declared unsafe to ride after a service. Our holiday to the USA was some well needed rest along with fun on various camping trips (with family and with Cub Scouts). There were a few weddings this year and plenty of socials with friends old and new. On the subject of Cub Scouts it was great awarding several of the Cubs leaving with the highest award they can achieve, the Silver Chief Scout Award, it has taken us a few years to get organised, but it lovely to see them work hard and be rewarded for it.<\/p>\n<p>So with the busy year almost done (just Christmas and 2 year old birthday to celebrate), I will see you all next year. Enjoy your Christmas and the celebration of our Saviour being born! (or however you celebrate!)<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-296","title":"My 2025 year in review","summary":"A round up of the last year as I checkout till the new year","date_modified":"2025-12-24T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#review"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-295","content_html":"<p>I discovered a new pattern in using one of the latest features from Django 6.0, template-partials. I probably need to give Claude some credit along with Tailwind Plus for the initial HTML. Earlier this week I was building out a modal pattern for the latest project I am building. You click a button, a modal pops up and loads a form via HTMX. Then submit the form and modal closes with the response triggering other HTMX updates on the page. I was building this out in tandem with Claude Code, with the starting point being a modal from Tailwind Plus which I thought only included the modal, but they also included the button to trigger the modal as well. Well the AI took this and ran with and essentially ended up with a Django template that looks like this:<\/p>\n<pre class=\"language-django\"><code class=\"language-django\">\n<span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{%<\/span> <span class=\"token tag keyword\">partialdef<\/span> <span class=\"token variable\">trigger<\/span> <span class=\"token delimiter punctuation\">%}<\/span><\/span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>button<\/span> <span class=\"token attr-name\">hx-get...<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>...<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>button<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n<span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{%<\/span> <span class=\"token tag keyword\">endpartialdef<\/span> <span class=\"token variable\">trigger<\/span> <span class=\"token delimiter punctuation\">%}<\/span><\/span>\n\n\n<span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{%<\/span> <span class=\"token tag keyword\">partialdef<\/span> <span class=\"token variable\">modal<\/span> <span class=\"token delimiter punctuation\">%}<\/span><\/span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dialog<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>form<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n        ...\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>form<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dialog<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n<span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{%<\/span> <span class=\"token tag keyword\">endpartialdef<\/span> <span class=\"token variable\">modal<\/span> <span class=\"token delimiter punctuation\">%}<\/span><\/span>\n<\/code><\/pre>\n<p>I don&#x27;t think I would have ever thought to have included the button section as a partial in this context, but I like the results since both can be used with the <code>include<\/code> tag at different points and you have locality of behaviour if you want to update some aspect that affects both. I&#x27;m less likely to need to jump around to every button to add an attribute for example.<\/p>\n<p>This got me thinking though, where else can partials fit in? Part of the magic with partials is the hash reference in the string reference to a template file. This means partials can be used in templatetags, earlier this year I wrote about using <code>simple_block_tag<\/code> in combination with <code>get_template<\/code> to produce a component like interface to build about a navigation. With partials I can merge the 3 small template files into a single template file with 3 partial templates. I think this is just the start for partials and template tags and I&#x27;m keen to explore their intersection even more in the coming weeks and months.<\/p>\n<p>Finally the other area which Carlton recently hinted at on Mastodon is in the area of Form rendering. Django 4.X saw the refactoring of Form rendering into the FormRenderer class. This class allows the specification of snippet template files to customise how various form and form elements are rendered at a variety of levels from global down to individual forms. Again we can specify partials here instead of individual templates. I need to work on this a bit more, but the hope is a single file of partials that is something of a spritesheet of form components instead of jumping around multiple files. We can then use partials to deduplicate these templates.<\/p>\n<p>Here is a quick example of my PartialsFormRenderer:<\/p>\n<pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">from<\/span> django<span class=\"token punctuation\">.<\/span>forms<span class=\"token punctuation\">.<\/span>renderers <span class=\"token keyword\">import<\/span> TemplatesSetting\n\n<span class=\"token keyword\">class<\/span> <span class=\"token class-name\">PartialsFormRenderer<\/span><span class=\"token punctuation\">(<\/span>TemplatesSetting<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    form_template_name <span class=\"token operator\">=<\/span> <span class=\"token string\">&quot;forms\/form_snippet.html#form&quot;<\/span>\n    field_template_name <span class=\"token operator\">=<\/span> <span class=\"token string\">&quot;forms\/form_snippet.html#field&quot;<\/span>\n<\/code><\/pre>\n<p>I am excited for what comes next and how much we could achieve with Django features alone!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-295","title":"More adventures in building template components in Django","summary":"Unlocking more possible patterns with template partials","date_modified":"2025-12-19T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#templates","#partials"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-294","content_html":"<p>Below is a tiny javascript snippet to add to the end of the <code>admin\/base.html<\/code>. This little snippet with ensure the current app in the left hand navigation is always at the top of the viewport when navigating between admin pages. Very handy when you have lots of apps and models!<\/p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token operator\">&lt;<\/span>script<span class=\"token operator\">&gt;<\/span>\n  <span class=\"token keyword\">const<\/span> current_app_nav <span class=\"token operator\">=<\/span> <span class=\"token dom variable\">document<\/span><span class=\"token punctuation\">.<\/span><span class=\"token method function property-access\">querySelector<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;.current-app&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n  current_app_nav<span class=\"token punctuation\">.<\/span><span class=\"token method function property-access\">scrollIntoView<\/span><span class=\"token punctuation\">(<\/span><span class=\"token boolean\">true<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token punctuation\">{<\/span><span class=\"token literal-property property\">block<\/span><span class=\"token operator\">:<\/span> <span class=\"token string\">&#x27;center&#x27;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token literal-property property\">container<\/span><span class=\"token operator\">:<\/span> <span class=\"token string\">&#x27;nearest&#x27;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token literal-property property\">inline<\/span><span class=\"token operator\">:<\/span> <span class=\"token string\">&#x27;nearest&#x27;<\/span><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n  <span class=\"token dom variable\">window<\/span><span class=\"token punctuation\">.<\/span><span class=\"token method function property-access\">scrollBy<\/span><span class=\"token punctuation\">(<\/span><span class=\"token number\">0<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token operator\">-<\/span><span class=\"token number\">100<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n<span class=\"token operator\">&lt;<\/span><span class=\"token operator\">\/<\/span>script<span class=\"token operator\">&gt;<\/span>\n<\/code><\/pre>\n<p>Enjoy!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-294","title":"Stop scrolling the admin apps","summary":"For my sanity...","date_modified":"2025-11-28T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#admin","#javascript"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-293","content_html":"<p>In my current project, I&#x27;m going to be storing a fair number of different API credentials, first those that we own and then plenty of OAuth credentials from our customers. Today I am pondering the first scenario and wondering what how I can store these securely and with the prinicple of least privilege.<\/p>\n<p>Currently I&#x27;m using django-allauth&#x27;s socialaccount models to store these details, it&#x27;s a nice abstraction and I can add relationships from my models to the models allauth provides to dynamically select the appropriate credentials I need to access the relevant API. However the secrets are stored in plain CharFields and are accessible from the admin. That&#x27;s fine for now, but soon that&#x27;s not going to fly.<\/p>\n<p>I could store these as environment variables, but this goes against the current dynamic design of holding configuration in the database, which allows me to switch API clients and tokens without a code deploy. Last night I was listening to a 1password advert about their SDK which goes beyond a CLI interface allowing developers to interact with their platform. Bitwarden (my password manager of choice) also has this capability and this got me wondering, what would Django integration to these providers look like?<\/p>\n<p>Off the top of my head there are a few possible integration points. First is via settings where we could dynamically allow Django access to a secret or set of secrets and then we only have to store a single access point outside of the manager; This is useful, not aligned to the design above. The other two integration points would be having either a SecretManager Model or SecretField. The model would provide a local proxy to interact with secrets (eg store extra metadata or related to from other models), but a certain fields (SecretField etc) would call out to the SDK to retrieve secrets, usernames or other details stored in the secure vault. The SecretField implementation is a smaller implementation that could be added to other normal models, where the local database stores the reference to the secret and then provides access to the secret via the SDK.<\/p>\n<p>The interesting design here would be providing a python API that would allow a developer to choose a secrets backend for this model or field. I have listed 2 above, but I know the larger cloud providers have services like this, Hashicorp has Vault, Kubernetes likely has something as well, and there should probably be an option to fall back to a file or environment variables.<\/p>\n<p>I would be interested to hear of similar work or packages if they exist! (A quick search on django packages reveals at least <a href=\"https:\/\/django-opfield.westervelt.dev\/en\/latest\/\">one package<\/a>)<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-293","title":"Django and password manager SDKs","summary":"What integrations could exist...","date_modified":"2025-11-26T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#package","#bitwarden","#onepassword"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-292","content_html":"<p>A few weeks ago I noticed a toot from Jeff Triplett about Anthrophic releasing <a href=\"https:\/\/www.claude.com\/blog\/claude-code-on-the-web\">Claude Code for the Web<\/a>. This was the final spark that coalesced a few different thoughts that had been lingering in the back of my head, some of I have written a bit about before. The first thought was the speed of prototyping that agentic AI enables. Essentially ideas or tasks can simply be executed rather than being written down and the time allocated to develop these ideas go from weeks to days or even hours. The second thought is related to the first in that tools like Agent OS allows for AI to build out products in a more reliable way for the most part. I have also been pondering how I can use my mobile more as an engineer, the Github app is ok for PR reviews, but to date building anything needs a larger screen.<\/p>\n<p>The final thought goes back to the toot from Jeff and Claude Code on the being possibly the cloest thing so far to my <a href=\"\/100-words\/day-282\">post from day 282<\/a> about how our tooling doesn&#x27;t yet fully leverage what AI can do for us.<\/p>\n<p>Well this led to me creating two things this week. First was a <a href=\"https:\/\/github.com\/nanorepublica\/aos-template\">template repo<\/a> on Github which is only loaded with an install of my Django Agent OS profile. This enables me to quickly start a project in the browser without having to open a terminal or potentially even be at my laptop, I could start a project from my phone. Second was an experiment to see how much I could get Claude to build from the browser. I took my idea from <a href=\"\/100-words\/day-71\">Day 71<\/a> about analysing a Django codebase for dead code that could be removed. Over the course of about 2 hours of my time and letting Claude along with Agent OS, I have a package released on PyPI.<\/p>\n<p>The unlock here is that I have yet to clone the repo to my laptop. In fact, the most time consuming part has been getting CI to work nicely to release new versions. Upon reflection this is something to go into the template repository, but then not every project needs to be uploaded to PyPI.<\/p>\n<p>It&#x27;s been a fun experiment to get a working proof of concept out the door so quickly, but it needs a bit more refinement, testing and review before I recommend anyone else use it! If you want to have a peak, the repo is <a href=\"https:\/\/github.com\/nanorepublica\/django-deadcode\">here<\/a> and package is <a href=\"https:\/\/pypi.org\/project\/django-deadcode\/\">here<\/a><\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-292","title":"django-deadcode: idea to release in under 2 hours","summary":"How much an Claude do...","date_modified":"2025-11-13T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#package","#ai","#experiments"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-291","content_html":"<p>I realised this week after a <a href=\"https:\/\/indiehackers.social\/deck\/@NoyanK7u@fosstodon.org\/115412956009445032\">short conversation<\/a> on Mastodon that I haven&#x27;t ever shared my personal Django journey, so hear goes!<\/p>\n<p>As a recent graduate in 2012, I first encounter Django while working at Ocado Technology, tasked with building internal tools for other developers. I was shown the ropes of Django by Ben Cardy (@benbacardi) and Mike Bryant. Essentially this was about putting a frontend to some scripts which would provision users and allow them to upload their ssh keys. This progressed to automating application provisioning with some hackery using the rubypython package if I remember correctly and storing data in an LDAP database. I started using Django 1.4, explored packaging these projects into .deb files, setup an internal pypi instance and tried to created a unified UI package across multiple projects. Finally we did start to open source a few packages on Github.<\/p>\n<p>2015 saw me leave Ocado to join a small charity startup using Django. Here I joined Bruno Alla (@browniebroke) building out a Django application hosted on heroku. For a long time it was just us two as developers, but this eventually grew to a team of 6. Again we published a few packages that we could split from the main codebase, although most of these have not grown in popularity. It was around this time I was becoming more aware of the mailing lists and contributing back to packages we used in the ecosystem.<\/p>\n<p>I left the startup in 2019 to go freelance, setting up Software Crafts, my own limited company. At the time I still wasn&#x27;t sure what my ideal client would be and agonised over this for years before realising Django was the through line of my career and my passion when it came to building software! I have had numerous clients over the last 6 years, mostly with small teams or startups, either building something fresh or continuing off from where other developers had left with little to zero handover. During this time I realised the community had started a Discord server and so I joined in July 2022 and started answering questions and helping out.<\/p>\n<p>The next year in 2023 I was asked to be a moderator and also attended DjangoCon Europe for the first time, which was an absolute blast. It was in this year that I started hosting a Django Social event each month in Cambridge, so generally getting very much more involved in the community. I also started donating regularly to the DSF mid 2023. 2024 saw me start contributing in the form code when I applied and got into one of the Djangonaut Sessions.<\/p>\n<p>Towards the end of 2024 I proposed the Online Community Working Group which after many rounds of comments and iteration was approved earlier this year. We are still getting the ball rolling on this, but I hope to have an announcement before the year finishes! I also had the opportunity to attend DjangoCon Europe again this year which was again was lovely to meet new friends from online and old friends from the last conference, as well as take the stage for a lighting talk on the last day. I recently was also made an admin of the Discord server! In terms of my career, 2024 saw me accept the opportunity to become co-founder of a new startup which I have been building this year. This of course is built using Django and leveraging the latest patterns (HTMX &amp; partials etc).<\/p>\n<p>I see a bright future for Django, I&#x27;m going to continue to contribute my time, energy and finance to the community, currently focusing on improving our online spaces which is key for those who cannot join the conferences or other in-person meetups (for which there are many). I also hope to get to more in-person events myself in due course!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-291","title":"An Introduction of sorts","summary":"My personal Django journey","date_modified":"2025-10-22T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#career","#community"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-290","content_html":"<p>Earlier this year, how to use Agentic AI\/LLM &#x27;clicked&#x27; in my head, mainly when I tried out Zed&#x27;s agentic mode and it could take a codebase as context and do simple tasks for me to review. This was great for adding admin classes to my existing project or creating <code>__str__<\/code> methods for my models. But I often found myself going in circles when building out a larger feature. Over the summer Brian Casel launched <a href=\"https:\/\/buildermethods.com\/agent-os\">Agent OS<\/a> and along with it came another term - spec-driven development. It took me some time to get my head around the concept, but I really have gotten in to the flow over the last month.<\/p>\n<p>Using Agent OS has allowed me to build out features with remarkable speed. Features that probably would have taken weeks were compressed into days or even hours. The key with this process is getting the AI to have layers of context (standards, product, specs) and it starts way before code, most of my time is spent reviewing markdown files, then it&#x27;s prompting the AI of choice to execute a task one at a time (or a few in a row) with me reviewing the output and making manual adjustments or creating a custom prompt if needed. The key to consistency here is to ensure any new decisions are recorded back into the appropriate layer (standards, product or specs). Agent OS does this by creating the specs and tasks in the first place and then adds verification documentation as it&#x27;s executing tasks.<\/p>\n<p>For me though, it&#x27;s understanding the meta framework that Agent OS provides which could be applied to other industries interacting with API. As I have mentioned there are 3 layers of context built into Agent OS. They are standards, product and specs. Really these are 3 layers of specificity of context. Standards are generally global pieces of information that are relevant to a user, in Agent OS this is how you code, your tech stack. But say in photography, this could be your camera, the editing tools you use, your prefered styles. Then we have a product. When programming this is a project, but again in photography this would likely be a particular client or type of shoot you offer. Finally we get to the specs, specs in coding are the documentation of how to build a feature, again relating to photography this is processing a singular photoshoot for a client. This type of layering I can see being applicable across other knowledge based industries, for example one of my clients is in sports nutrition, they are exploring AI and I can see this layered framework being applicable to them in how they add AI features to more reliably do the same thing each time for a coach.<\/p>\n<p>I&#x27;m excited to try Agent OS 2.0 this week, which has sub-agents when used with Claude Code. The use of sub-agents again renforces the meta framework as I hand off different specialised tasks to allow me to focus on the larger product being built. One final important note is that we need to start focusing on documentation, particularly our standards, they form the basis for an LLM to write code similar to what we would have written in the first place.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-290","title":"Developing and building with AI","summary":"How frameworks are key to consistency and productivity","date_modified":"2025-10-15T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#ai","#frameworks","#spec_driven_development","#llm"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-289","content_html":"<p>While lamenting that I&#x27;m not in Spain for Django on the Med (but do have other events to go to), I have been reading the recent forum thread about <a href=\"https:\/\/forum.djangoproject.com\/t\/django-needs-a-rest-story\/42814\">Django &amp; REST<\/a> and it has provoked some thoughts, some of which I shared directly in the thread, but I hope to expand on them here and likely in another post as I&#x27;m not quite done prototyping my ideas.<\/p>\n<p>First there is some obvious core work that needs to be done:<\/p>\n<ol>\n<li>Document the current options as a how-to\/guide for building with Django and some measure of guidance for how to choose perhaps. Personally I would be happy with getting an AI agent to do a first pass of a page like this.<\/li>\n<li>Get the in progress tickets finished. That mostly being modernising content negotiation in the request object.<\/li>\n<\/ol>\n<p>Once we have those in place we are a starting point to focus the main components that make up an API in Django. Primarily it&#x27;s as follows:<\/p>\n<ol>\n<li>URLs or groups of URLs<\/li>\n<li>Views or groups of URLs<\/li>\n<li>Serializing data &amp; deserializing data.<\/li>\n<\/ol>\n<p>Most of the focus has mostly been on the serialization component, in terms of which option to pick to include or not include, what options are available (or which there are many). However it strikes me that we could take a similar approach to that of the Tasks interface added in 6.0. We need be focusing on the <a href=\"\/100-words\/day-277\">Django API\/Protocol<\/a> for these serialization options to hook into regardless of which option you prefer for a project. This would also allow us to more easily keep up with the latest libraries as they progress in that area.<\/p>\n<p>So what would be required for this API for APIs? I would propose the following:<\/p>\n<ol>\n<li>A way to generate a set of urls to cover the CRUD HTTP methods<\/li>\n<li>A generic view layer system that allows the specification of what serialization method to use. My preference would be class-based, but we would want a function based option as well.<\/li>\n<li>Hooks to leverage ORM definitions of models for our serialization options to make use of.<\/li>\n<\/ol>\n<p>Based on the above I have been playing around with subclassing neopolitan&#x27;s <code>CRUDView<\/code> and had a peek at Emma&#x27;s <a href=\"https:\/\/gitlab.levitnet.be\/levit\/djrest\">djrest2<\/a> and have a class that looks like this for now:<\/p>\n<pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">class<\/span> <span class=\"token class-name\">APIView<\/span><span class=\"token punctuation\">(<\/span>CRUDView<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">get_serializer_data<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">,<\/span> serializer<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        <span class=\"token keyword\">raise<\/span> NotImplemented\n\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">serialize_many<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">,<\/span> qst<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        <span class=\"token keyword\">return<\/span> <span class=\"token punctuation\">[<\/span>self<span class=\"token punctuation\">.<\/span>serialize_one<span class=\"token punctuation\">(<\/span>obj<span class=\"token punctuation\">)<\/span> <span class=\"token keyword\">for<\/span> obj <span class=\"token keyword\">in<\/span> qst<span class=\"token punctuation\">]<\/span>\n\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">serialize_one<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">,<\/span> obj<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        serializer <span class=\"token operator\">=<\/span> self<span class=\"token punctuation\">.<\/span>serializer_class<span class=\"token punctuation\">(<\/span>obj<span class=\"token punctuation\">)<\/span>\n        serialized_data <span class=\"token operator\">=<\/span> self<span class=\"token punctuation\">.<\/span>get_serializer_data<span class=\"token punctuation\">(<\/span>serializer<span class=\"token punctuation\">)<\/span>\n        <span class=\"token keyword\">return<\/span> serialized_data\n\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">serialize<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">,<\/span> context<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        data <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">{<\/span><span class=\"token punctuation\">}<\/span>\n        <span class=\"token keyword\">if<\/span> self<span class=\"token punctuation\">.<\/span>role <span class=\"token operator\">==<\/span> Role<span class=\"token punctuation\">.<\/span>LIST<span class=\"token punctuation\">:<\/span>\n            data <span class=\"token operator\">=<\/span> self<span class=\"token punctuation\">.<\/span>serialize_many<span class=\"token punctuation\">(<\/span>context<span class=\"token punctuation\">[<\/span><span class=\"token string\">&quot;object_list&quot;<\/span><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">)<\/span>\n        <span class=\"token keyword\">if<\/span> self<span class=\"token punctuation\">.<\/span>role <span class=\"token operator\">==<\/span> Role<span class=\"token punctuation\">.<\/span>DETAIL<span class=\"token punctuation\">:<\/span>\n            data <span class=\"token operator\">=<\/span> self<span class=\"token punctuation\">.<\/span>serialize_one<span class=\"token punctuation\">(<\/span>context<span class=\"token punctuation\">[<\/span><span class=\"token string\">&quot;object&quot;<\/span><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">)<\/span>\n        <span class=\"token keyword\">return<\/span> data\n\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">render_to_response<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">,<\/span> context<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        <span class=\"token keyword\">return<\/span> JsonResponse<span class=\"token punctuation\">(<\/span>\n            content_type<span class=\"token operator\">=<\/span><span class=\"token string\">&quot;application\/json&quot;<\/span><span class=\"token punctuation\">,<\/span>\n            data<span class=\"token operator\">=<\/span>self<span class=\"token punctuation\">.<\/span>serialize<span class=\"token punctuation\">(<\/span>context<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n        <span class=\"token punctuation\">)<\/span>\n\n    <span class=\"token decorator annotation punctuation\">@classproperty<\/span>\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">url_base<\/span><span class=\"token punctuation\">(<\/span>cls<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        <span class=\"token keyword\">return<\/span> <span class=\"token string-interpolation\"><span class=\"token string\">f&quot;api\/<\/span><span class=\"token interpolation\"><span class=\"token punctuation\">{<\/span>cls<span class=\"token punctuation\">.<\/span>model<span class=\"token punctuation\">.<\/span>_meta<span class=\"token punctuation\">.<\/span>model_name<span class=\"token punctuation\">}<\/span><\/span><span class=\"token string\">&quot;<\/span><\/span>\n<\/code><\/pre>\n<p>This could then be used as follows:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token keyword\">class<\/span> <span class=\"token class-name\">ProjectAPIView<\/span><span class=\"token punctuation\">(<\/span>APIView<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    model <span class=\"token operator\">=<\/span> Project\n    fields <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">[<\/span>\n        <span class=\"token string\">&quot;name&quot;<\/span><span class=\"token punctuation\">,<\/span>\n        <span class=\"token string\">&quot;description&quot;<\/span><span class=\"token punctuation\">,<\/span>\n        <span class=\"token string\">&quot;created_at&quot;<\/span><span class=\"token punctuation\">,<\/span>\n        <span class=\"token string\">&quot;updated_at&quot;<\/span><span class=\"token punctuation\">,<\/span>\n        <span class=\"token string\">&quot;created_by&quot;<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token punctuation\">]<\/span>\n    serializer_class <span class=\"token operator\">=<\/span> ProjectSerializer\n\nurlpatterns <span class=\"token operator\">=<\/span> ProjectAPIView<span class=\"token punctuation\">.<\/span>get_urls<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>There are some very obvious holes in this current code, like the fact that I haven&#x27;t dealt with creations, updates &amp; deletions. My focus is creating a reasonable API at the view layer which various serialization options could plug into. There is also covering my 3rd point of ORM integration for the options that need it.<\/p>\n<p>I hope this in part keeps the conversation going and we can get some progress made in this area!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-289","title":"Django & REST & APIs","summary":"My thinking based on some renewed interest","date_modified":"2025-10-08T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#rest","#apis"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-288","content_html":"<p>For some reason I have been putting off writing this one, perhaps because it feels vunerable to me in this (public) context of my life. That said I am pushing through as I think there is some interesting content and idea&#x27;s to be had below.<\/p>\n<p>The content below started to form in my head from some questions posed by Tim Schilling in the Djangonaut Space Discord space. Session 5 has just started which is great, but his question was what to do around those that don&#x27;t get selected? My initial response to this was maybe something along the lines of mastermind groups that are fairly common in other communities, for me I have seen it within founder circles where those at a similar level come together to help each other out.<\/p>\n<p>What&#x27;s key for both Djangonaut Space and mastermind&#x27;s is that they are small groups. Carlton highlighted small groups in his DjangoCon Europe talk this year, especially in regards to trusting others. I have seen this with Moderators and Helpers in the Discord server, the smaller space allows for increased trust, which in turn allows for other conversations to happen that are not purely about Django, they are about our broader lives and that&#x27;s part of what creates community that lasts.<\/p>\n<p>But what does any of the above do with the Church? Well it&#x27;s some of the context the has been mulling around in my brain and a couple of weeks back I realised something. From wider meta perspective the Church (and likely other religions, although I more familiar with the Church) is fairly similar to Open Source Software. The Church has multiple different denominations that have different views on the details, but generally the believe in the Christian God and that Jesus was the Son of God. OSS is similar in that the main thing in making the source code available for others is a good thing to do. The Church can have small expressions and larger expressions, similar with OSS, there are small hobby projects and larger projects like Python or Django. Finally they are both forms of community made of imperfect humans trying (for the most part) to do their best and make this world a better place. My personal challenge here is to love a person as per my belief (love one my neighbour as myself) while not agreeing with what they stand for. There are plenty examples in both spaces of those that don&#x27;t share my point of view.<\/p>\n<p>So can we learn anything from the Church? It has existed ~100 times longer than Django so perhaps there are things to learn in how it does community.  It can vary wildly. but generally there is some regular meetings where teaching is given, around this teaching there are coffee drank and conversations had. It&#x27;s the entrance to the community, it&#x27;s easy to get come along and see if you personally like it. Beyond that there are volunteer opportunities to help others, but also small groups that meet regularly to do life together. A church may also have outreach events that are more social in nature or help those in need.<\/p>\n<p>It&#x27;s fairly easy to see that in the Django community we have volunteer&#x27;s that give their time either in a formal capacity on the Board, a Team, a Working Group or at a conference, but also informally by simply helping others in the community. I would propose one part that&#x27;s missing is smaller groups. Djangonaut Space is a start here, but it&#x27;s very easy for someone to drop off after wards. To some degree that&#x27;s always going to happen. Small groups are more about peer leadership and while might need some help from a central place to get started, each group would want to be self sustaining. It&#x27;s about having Django as the common thread (say perhaps starting to contribute), but it&#x27;s also doing life together, supporting each other through the ups and downs of life. This could be a longer term view of mentoring with each person bringing their problems and others helping to solve it. We&#x27;re a relatively big community so it&#x27;s easy to get lost and not feel like you belong. That&#x27;s where community needs to be smaller, for trust to build, to feel belonging.<\/p>\n<p>Another finaly interesting comparison is to look at the tools that exist to support churches from the backoffice. It&#x27;s a specific niche of software that has evolved, which would map fairly well to organising the Django community. The software exists to support the building of a community to an extent. The core compnents are tracking community members, organising community events, managing teams and small groups and finally managing donations. The Django Project website has beginnings of some of these elements and I&#x27;m excited to further build some of this functionality to enable us to build a stronger, deeper community for those that want it.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-288","title":"Doing community for the long haul","summary":"Contrasting OSS communities (specifically Django) with the Church","date_modified":"2025-10-01T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#community","#mentoring","#church"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-287","content_html":"<p>A few weeks back I wrote about a suggestion for a <a href=\"\/100-words\/day-283\">new warning in regard to makemigrations<\/a>, which got some feedback on the <a href=\"https:\/\/indiehackers.social\/deck\/@nanorepublica\/114986828316175985\">fediverse<\/a> some was positive, others a bit crictial of the other warning I was recently responsible for in 5.2. Anyway one suggestion from Carlton in that thread was interesting, the idea of app for beginners. It also reminded me of a conversation I had with Thibaud last year where he made a comment that with Wagtail features &amp; changes, they try to consider the potential infinite future developers of the software over the fixed number of past developers that have already used Wagtail. So having a place where we prioritise future users and developers over the existing backward compatibility conerns is an interesting one to me.<\/p>\n<p>The idea is simply to put all of these beginner style warnings in to an app specifically for beginners. This easily contains the extra code into a couple of lines of configuration (as much configuration as any other third-party package). This could be a simple thing that then tutorials\/books could recommend, it could possibly make it&#x27;s way into a starter template for Django itself (perhaps when <a href=\"https:\/\/github.com\/django\/deps\/pull\/98\">DEP 15<\/a> is implemented).<\/p>\n<p>The goal of this app would be two-fold, first have a place to put warnings, helpful views or other code and common configuration that often trips up beginners. This code could even recommend or install other third-party packages. Some of this code would be operational (eg the warnings) and other aspects could be reference (eg views, forms or models). The secondary goal would be a place for us to experiment with ideas before they move to core, but get broad usage from new developers.<\/p>\n<p>I think is something I will experiment with in the near future once I have completed a few other Django related tasks I have on my list!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-287","title":"Proposal: django.contrib.beginners","summary":"An app to guide beginners","date_modified":"2025-09-08T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#thirdPartyPackage","#django","#contrib"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-286","content_html":"<p>I have been slowly working on my package django-prodserver which creates a Django management command API to interact with production processes. This package is directed at beginners but others may find it nice to have process configuration stored inside your settings file.<\/p>\n<p>Out of the box it supports:<\/p>\n<ul>\n<li>gunicorn<\/li>\n<li>uvicorn<\/li>\n<li>waitress<\/li>\n<li>celery<\/li>\n<li>django-tasks<\/li>\n<\/ul>\n<p>All of the above are available as package extras so can be installed as follows (or combinations):<\/p>\n<pre class=\"language-bash\"><code class=\"language-bash\">pip <span class=\"token function\">install<\/span> django-prodserver<span class=\"token punctuation\">[<\/span>gunicorn<span class=\"token punctuation\">]<\/span>\npip <span class=\"token function\">install<\/span> django-prodserver<span class=\"token punctuation\">[<\/span>celery<span class=\"token punctuation\">]<\/span>\npip <span class=\"token function\">install<\/span> django-prodserver<span class=\"token punctuation\">[<\/span>uvicorn<span class=\"token punctuation\">]<\/span>\npip <span class=\"token function\">install<\/span> django-prodserver<span class=\"token punctuation\">[<\/span>waitress<span class=\"token punctuation\">]<\/span>\npip <span class=\"token function\">install<\/span> django-prodserver<span class=\"token punctuation\">[<\/span>django-tasks<span class=\"token punctuation\">]<\/span>\n<\/code><\/pre>\n<p>I have also created a command <code>devserver<\/code> which is a simple rename of <code>runserver<\/code><\/p>\n<p>Source Code (&amp; Issues &amp; PRs): <a href=\"https:\/\/github.com\/nanorepublica\/django-prodserver\">https:\/\/github.com\/nanorepublica\/django-prodserver<\/a><\/p>\n<p>Docs (needs work): <a href=\"https:\/\/django-prodserver.readthedocs.io\/en\/latest\/\">https:\/\/django-prodserver.readthedocs.io\/en\/latest\/<\/a><\/p>\n<p>My learnings on this package (as you can clearly see from the changelog) was the release process! A note to myself to work on this if I hope to produce more packages (and not simply talk about them)<\/p>\n<p>I would welcome feedback on this package as I would like to propose this gets merged into core at some point down the line (no rush on this of course). I would be especially keen to get Issues\/PRs for any more common production processes I have missed from the package.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-286","title":"django-prodserver is live","summary":"...and ready for consumption and opinions","date_modified":"2025-08-29T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#third-party-package","#django","#production"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-285","content_html":"<p>Another short one today, that is a pattern I have noticed in a couple pieces of software I use, notably Todoist, Slack &amp; Vivaldi. All three of these allow a user to configure the menu options to some degree. Slack has the option to customise the navigation options shown within a particular workspace to optimise the experience for a user. Todoist takes this a step further in the mobile app to allow a user to sort the menu items.<\/p>\n<p>Browsers have always had a great experience of customisation, but Vivaldi takes this to an awesome extreme by allowing a user to customise each and every possible context menu, giving true flexability to their users.<\/p>\n<p>Personally I have never considered the power of this and wonder if there are any efficient implementation of this for Django without creating a huge amount of complexity. The naive default solution would likely involve a model and a context processor and\/or a middleware, it might be something I add in my next project, if we feel it would be beneficial to our users.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-285","title":"Configurable UI in Software","summary":"Considerations for power users","date_modified":"2025-08-18T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#software","#ui"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-284","content_html":"<p>I am starting to feel like I have written about this too much as this point, but anyway, third-party packages again!<\/p>\n<p>Recently I have been wondering about why third-party packages exist within the Django ecosystem. Broadly there is a few categories<\/p>\n<ul>\n<li>Establishing some new functionality that doesn&#x27;t exist in core (APIs, Feature flags, Payments, etc)<\/li>\n<li>Extending an existing API with a new backend (databases, caches etc)<\/li>\n<li>Utilities to help with development (perhaps this is a subset of the first point)<\/li>\n<li>Packages demonstrating functionality that would be desirable with in Django core.<\/li>\n<\/ul>\n<p>Now one huge benefit to third-party packages is that it allows for choice, be it in the tools and dependencies used, the design and scope of the code. This is both for the package creator\/maintainer and the user of said package. However I want to focus in on the last usage above and question whether choice is a good thing for this type of package.<\/p>\n<p>If we were to build a package with the goal of it perhaps one day being merged into Django, would it not benefit our future selves and others if the overall design of the package matched Django conventions and standards whereever possible? For example, choosing unittest over pytest, or minimising the number of external dependencies that would be classed as unnecessary?<\/p>\n<p>This has led to me to the idea of creating an &#x27;official&#x27; template repo or cookiecutter template (or someother tool) that produces a repo for packages like this. Is it an extra maintanence burden? Yes, but I think it would greatly improve the new contributor story when they are told to create a package for their idea. And when I say official, ideally it would be within the Django Github organisation, but if it started in another place, say Django-commons, that would also work. I do wonder how it could be worked to lessen the maintenance burden though?<\/p>\n<p>Good idea or not?<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-284","title":"Standards for third-party packages","summary":"Establishing conventions for easier integrations","date_modified":"2025-08-14T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#contributing","#third-party_packages","#packages"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-283","content_html":"<p>Could our tools be smarter (even without AI) and helpful to prevent footgun usage? Today I&#x27;m taking aim at the Django <code>makemigrations<\/code> command. I feel fortunate to have been introduced to South and Django migrations at the beginning of my career so the logic of the migration files and the workflow makes sense in my head (or if I was confused I was corrected very early on by colleagues). However I wonder what other tech stack&#x27;s do in regard to keeping keeping code and schema&#x27;s in sync.<\/p>\n<p>I question this as every so often I run into newcomer&#x27;s to Django not commiting migration files to source control and running <code>makemigrations<\/code> in every environment, which if you didn&#x27;t know is a very bad idea that will lead to numerous issues as the project progresses. This led me to the following question:<\/p>\n<p>Could we prevent this happening in the first place, or place a burden on those knowing the risks when taking them?<\/p>\n<p>My immediate answer to this is spit out a warning if someone tries to run <code>makemigrations<\/code> when the <code>DEBUG<\/code> setting is False. To me this should be the minimum to add to the command. Using the DEBUG setting is a proxy for any environment that isn&#x27;t development, it won&#x27;t be a 100% accurate, but close enough I feel. (Side thought, what about a setting for named environments?)<\/p>\n<p>Going further we could even exit the command early if <code>DEBUG<\/code> is False or show a prompt similar to <code>collectstatic<\/code> or even require setting a new environment variable to allow the check to be bypassed when <code>DEBUG<\/code> is False.<\/p>\n<p>Additionally when the command is run successfully could we add a message along the lines of <code>Be sure to commit these files to your version control tool of choice<\/code> perhaps even doing a bit of introspection of directories to show the relevant commands in the message?<\/p>\n<p>Any other thoughts on this? I am now half expecting to hear of all the legimate use-cases for running <code>makemigrations<\/code> when DEBUG is False! But I think this would be a useful addition to the command to prevent a footgun that happens fairly often for beginners.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-283","title":"A beginner check for makemigrations","summary":"Another mistake I see time and time again","date_modified":"2025-08-07T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-282","content_html":"<p>First a disclaimer on this one: I am making the assumption that the AI trend is here to stay in some form and an economic crash\/bubble doesn&#x27;t make the usage of them untenable, also I have yet experiment with every tool out there!<\/p>\n<p>With that said, a brief personal history of my usage of LLM&#x27;s and the current wave of AI. I tried out ChatGPT when it was first released and was fairly impressed by the results, but the cruical missing step for me was the lack of browser integration, searching Google was still much quicker from a new tab page and the results from ChatGPT felt isolated, there was too much friction in my workflow for it be usable. I tried out a different product (I forget the name), which allowed me to search from a new tab page and I got AI results and normal search results in one go. This was better, but it still didn&#x27;t stick, and so I kept experimenting with the tools on an ad-hoc basis, solving small challenges, but it not being a daily driver. In this I experimented with local LLMs and Zed&#x27;s AI integration.<\/p>\n<p>This changed earlier this year where I experimented with Manus.im and using Claude with Zed&#x27;s agent mode. Both of these unlocked ideas or wrote decent code directly into my project for me to review, saving me time that I could measure. Since then I have used Zed&#x27;s agent mode more frequently, that said I do still enjoy coding myself so sometimes forget to use the tools available.<\/p>\n<p>This daily to weekly usage has led me to consider what an AI-first IDE would look like. At this point the newly released Kiro or Cursor comes to mind or others such as Zed or VSCode plus extensions, they are all really designed for a developer-first point of view since their beginning&#x27;s were from a time before agent workflows existed.<\/p>\n<p>Personally I am looking forward to the IDE that is built ground up to create, run and manage agents that follows the trend of &#x27;spec-driven-development&#x27; that has started to form recently within AI circles. Generally I would expect the following:<\/p>\n<ul>\n<li>that the tool to have viewing and editing files (like we are used to) come secondary to managing a number of different agents, reviewing their output as they finish, with the UX to be something closer to one of the many git GUI tools around or Github&#x27;s PR review experience.<\/li>\n<li>the rules to be part of the onboarding experience of a project (similar to the interactive prompts when setuping a project on the command line - eg cookiecutter) and allow for fluid automatic updates as part of a conversation flow. Perhaps even a rules generator for existing projects by scanning files and styles.<\/li>\n<li>The prompt window would be, of course be the main window and not off to the side.<\/li>\n<li>A search for semantic inclusion of elements over files would be the default allowing for specific, precise context when creating new code.<\/li>\n<li>A mobile experience for reviewing agents would also be key, perhaps the tool would be mobile-first even? The mobile experience for me would be key to allow reviewing code on the go when I am away from the desk.<\/li>\n<li>Finally we would likely still need to normal editing experience, however switching back to a traditional editor would be sufficient for me.<\/li>\n<\/ul>\n<p>Is this something of a wishlist? Yes, but I could easily a version of this starting soon, because there a paradigm shift coming I think, where the day to day activity goes from one of writing on our local machines, to one of review on our local machines being the priority. Let me know what you think!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-282","title":"Our tools are still not designed for the AI future","summary":"What I would like to see someone build","date_modified":"2025-07-29T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#ai","#tooling","#future"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-281","content_html":"<p>Yesterday I finally finished and released the <a href=\"https:\/\/youtu.be\/Lv5HTmhQ-6I\">latest feature<\/a> of Comfort Monitor Live, which allows users to design custom layouts for the comfort monitor.<\/p>\n<p>This has been build has been a long slog of over 6 months slowly working on it every Wednesday evening for a couple of hours. AI has helped to an extent in building this feature, but mostly it was still me slowly building and debugging issues as they came up. Being a chrome extension, means a lot of Javascript and I the UI has been NextJS. Both of these have made me realise the beauty and speed Django brings to a project for CRUD operations against a datastore and a structure for quickly creating a UI that can store that data.<\/p>\n<p>This was also one of those features which resulted in a rebuild of the core logic to be better and scalable for future use, especially the next feature which will allow the user to implement some rules around how the comfort monitor runs during an event. It&#x27;s going to be another big lift when I do build it, but for now, with this shipped it&#x27;s going to allow me to focus more on some Django contribution work, namely django-prodserver, an open PR for a messages ticket and work on the Online Community WG.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-281","title":"Latest feature of Comfort Monitor Live released!","summary":"This look so much longer than I thought it would...","date_modified":"2025-07-24T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#layout_designer","#comfort_monitor_live","#react","#NextJS","#chrome_extension"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-280","content_html":"<p>Over the weekend Django celebrated being <a href=\"https:\/\/www.djangoproject.com\/weblog\/2025\/jul\/13\/happy-20th-birthday-django\/\">20 years<\/a> since the first public commit. This is an incredible achievement for a community led project. Django is behind some tiny projects to those that scale globally and personally my career wouldn&#x27;t be where is it without Django.<\/p>\n<p>Most of my career existed with only a vague awareness of the community, but since getting involved on Discord and beyond has been great for my soul and enjoy those that share the passion of seeing Django succeed.<\/p>\n<p>So as we celebrate 20 year&#x27;s would be to get involved if you use Django, go to an event, donate to the DSF or join us in the community (online and in person). If you work for a company that use&#x27;s Django lobby them to donate as well! I am excited for the next decade of slow but sure progress and the community being healthier than ever before.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-280","title":"Happy Birthday to Django!","summary":"To the next decade or two","date_modified":"2025-07-14T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#birthday"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-279","content_html":"<p>So this is a bit of a follow on from <a href=\"\/100-words\/day-277\">Day 277<\/a> and taking the premise of the idea presented with it and pushing it further. What if we, as a community decided to (re-)write Django in another language?<\/p>\n<p>It&#x27;s not as wild as you might think, Lily Foote is currently tackling an implementation of the Django template language in Rust and someone else suggested that URL resolving might benefit from a similar treatment. However that is not the goal of this push forward, but it is again Django as design pattern or set of API&#x27;s. If we wanted to allow someone to migrate Django (or even part of it) to a new language, some comphrensive API documentation outside the codebase and inside the codebase would be a good start.<\/p>\n<p>And as I write this I realise that we do have this, it&#x27;s the amazing test suite that helps to make Django stable (that&#x27;s all 17898 tests and counting), but even then a test suite is never the whole story.<\/p>\n<p>Today was more of a pondering thought and not a complete one at that, but more of a thought experiment and a consideration (to myself more than anyone) of what Django is and what can be going forward.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-279","title":"What if Django was written in a new language..","summary":"just a bit of the thought experiment","date_modified":"2025-07-10T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#api_design"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-278","content_html":"<p>Well it&#x27;s been more than a few weeks since I last managed to get some words out the door or even have the headspace for writing. This was mostly down to the new startup picking up steam and client work taking up time, but also a wedding, holiday&#x27;s and other priorities coming in for me to deal with! All that to say, I&#x27;m back for a little while at least, summer is fast approaching which means holiday&#x27;s again.<\/p>\n<p>I&#x27;ll keep today short as I don&#x27;t have a particular topic fully developed in mind, but few things I have in my head recently are:<\/p>\n<ul>\n<li>AI has finally clicked in my head especially with Agentic stuff coming through. This is what I expected a few years back when ChatGPT was released. The true skill these days seems to be ticket writing (although AI developing PRD documents is something I need to try out). Also using git worktrees to allow multiple agents working on my projects at the same time and creating my own custom AI agents..<\/li>\n<li>I have been enjoying, with the help of AI, the python attrs and cattrs library to validate data going to and from Stripe.<\/li>\n<li>I&#x27;m excited by some active Django topics that are exploring what it means for some items to partially included into core, perhaps via pip-extras or some other mechanism<\/li>\n<li>How payments work<\/li>\n<li>How card transactions work<\/li>\n<li>Open-sourcing a codebase that would typically be closed-source.<\/li>\n<li>The rough edges of AI usage and how my work is changing (could it all be mobile based?)<\/li>\n<li>Building on my last blog post, could we document the Django API&#x27;s enough so that it could be implemented in another language? The point being the complete documentation, not necessarily the rewrite itself<\/li>\n<\/ul>\n<p>That&#x27;s all for now! Hopefully another post soon on another Django related topic!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-278","title":"I am back","summary":"it has been a busy month...","date_modified":"2025-07-01T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#life_update","#work"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-277","content_html":"<p>This has been a concept that has been brewing in my mind for the last couple of months. It&#x27;s been mostly inspired by my desire to see a <code>prodserver<\/code> (final name TBC) in Django Core alongside <code>runserver<\/code> and why it sitting happily as third-party package would, to me, not be a sufficient solution.<\/p>\n<p>As part of some research on potential names for commands I dug into the django-tasks DEP to understand how the worker would be started. My <a href=\"https:\/\/github.com\/django\/deps\/blob\/main\/accepted\/0014-background-workers.rst#future-iterations\">findings<\/a> found that it will be currently left to future iterations with the current solution delegated to the implementation (read a third-party package).<\/p>\n<p>Additionally Carlton&#x27;s talk at DjangoCon Europe this year teased at the idea of modularization, how can we break up Django for some aspects to ship faster. I too have toyed this with in the past in regard to the <a href=\"\/100-words\/day-199\">contrib<\/a>) <a href=\"\/100-words\/day-200\">module<\/a>. Carlton has also written about Django having a grain, a natural way of working, to the extent that I would say we have Djangonic?! code in addition to code being Pythonic.<\/p>\n<p>Finally to reference the Zen of Python:<\/p>\n<blockquote>\n<p>There should be one-- and preferably only one --obvious way to do it.<\/p>\n<\/blockquote>\n<p>This has become harder to see as time as gone on with the number of third-party packages exploding in some areas. However their are some clear exceptions to the confusion, third-party database backends will generally have a clear choice (maintenence dependent), the other would be storage backends, cache backends or template backends. The commonality here is that Django provides a very clear API surface and settings pattern to integrate with. Yes, you still have the choice in which database, storage or cache to choose from, but when it comes of how to do it in Django it&#x27;s very clear.<\/p>\n<p>So the main responsibility of Django Core is to fulfil that line from the Zen of Python, by continuing to provide a clear one obvious way to get a job done with most implementation details existing outside of core. When you view Django in this light it is definitely less about the code, but the API Design decisions that have led to a Protocol for creating web applications using Python.<\/p>\n<p>... and yes I do think prodserver would fill that design gap for specifying how production processes should be started (along with simple-deploy for deployments). \ud83d\ude09<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-277","title":"Django is not a framework but a Protocol","summary":"... or an API or a design pattern","date_modified":"2025-05-22T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#design","#thirdpartypackages"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-276","content_html":"<p>Well I have been meaning to write this for over 2 weeks now, but better late than never! Towards the end of April 2025 I attended the DjangoCon Europe conference and Sprints and it was brilliant and exhausting all in one go.<\/p>\n<p>Let&#x27;s begin with the travel there, I decided to join those doing the SailRail for a relaxed train ride and crossing the sea to Dublin. This was great as I managed to make some use of the day (work and a blog post) while travelling as well as having some travel companions in the form of Thibaud, Sage, Tom &amp; Daniele.<\/p>\n<p>The next day kicked off the conference with an excellent keynote from Sarah Boyce, and other talks followed thoughout the next 2 days. Databases was a big theme along with community engagement and HTMX. However for me it was walking into the room and meeting folks from the community in person, that I have interacted with online for the past couple of years. This was also coupled with great conversations with friends new &amp; old (mostly around making Django better). I also plucked up the courage and gave a lighting talk on the last day about my year of 100 words.<\/p>\n<p>The evening socials again were excellent! Django Social on Wednesday and the official party on Friday, with a more chill evening going climbing with a couple of interested attendees. The weekend brought the Sprints which were just perfect. I managed to crack on with an open ticket\/PR I have for the messages app in Django and also make some good progress on django-prodserver.<\/p>\n<p>It was sad to leave, but reminds me that I want to go next year (if I am allowed by the family!). I am also excited with the energy I felt across the week reminding me that Django is going strong as ever and the communuity has a bright future. I could write more, but I am aware that I need to crack on with today&#x27;s work, but I will leave you with the recommendation of getting to a DjangoCon if are use Django in any form, you will not be disappointed.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-276","title":"My second DjangoCon Europe","summary":"a very late review of my time in Dublin","date_modified":"2025-05-16T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#conference","#djangoconeurope25","#djangoconeurope"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-275","content_html":"<p>A quick one today! In Django 5.2 we have an awesome new feature of <a href=\"https:\/\/docs.djangoproject.com\/en\/5.2\/releases\/5.2\/#automatic-models-import-in-the-shell\">auto-imports into the shell<\/a>. This has existed in <a href=\"https:\/\/django-extensions.readthedocs.io\/en\/latest\/shell_plus.html\">django-extensions<\/a> for as long as I can remember, but it&#x27;s now in Django by default. That said the Django version only auto-imports your models and nothing else.<\/p>\n<p>To customise what gets auto imported then you need to override the management command by creating a file in your project somewhere with the path <code>management\/commands\/shell.py<\/code> and then override <code>get_auto_imports<\/code><\/p>\n<p>Below is snippet to mirror the imports from <code>shell_plus<\/code> in django-extensions, just copy\/paste and you no longer need to use <code>shell_plus<\/code>!<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token keyword\">from<\/span> django<span class=\"token punctuation\">.<\/span>core<span class=\"token punctuation\">.<\/span>management<span class=\"token punctuation\">.<\/span>commands <span class=\"token keyword\">import<\/span> shell\n\n\n<span class=\"token keyword\">class<\/span> <span class=\"token class-name\">Command<\/span><span class=\"token punctuation\">(<\/span>shell<span class=\"token punctuation\">.<\/span>Command<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">get_auto_imports<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        <span class=\"token keyword\">return<\/span> <span class=\"token builtin\">super<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">.<\/span>get_auto_imports<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">+<\/span> <span class=\"token punctuation\">[<\/span>\n            <span class=\"token string\">&quot;django.core.cache.cache&quot;<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;django.conf.settings&quot;<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;django.contrib.auth.get_user_model&quot;<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;django.db.transaction&quot;<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;django.db.models.Avg&quot;<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;django.db.models.Case&quot;<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;django.db.models.Count&quot;<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;django.db.models.F&quot;<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;django.db.models.Max&quot;<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;django.db.models.Min&quot;<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;django.db.models.Prefetch&quot;<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;django.db.models.Q&quot;<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;django.db.models.Sum&quot;<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;django.db.models.When&quot;<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;django.utils.timezone&quot;<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;django.urls.reverse&quot;<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;django.db.models.Exists&quot;<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;django.db.models.OuterRef&quot;<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;django.db.models.Subquery&quot;<\/span><span class=\"token punctuation\">,<\/span>\n        <span class=\"token punctuation\">]<\/span>\n<\/code><\/pre>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-275","title":"Matching auto-imports in Django 5.2 to django-extensions","summary":"a quick reference to switch over to the plain shell","date_modified":"2025-04-29T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#shell","#shell_plus","#management_commands"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-274","content_html":"<p>So today&#x27;s post was inspired by a fairly recent article from Swizec Teller where he explores <a href=\"https:\/\/swizec.com\/blog\/a-pattern-for-composable-ui-in-flask\/\">composable views in Flask<\/a>. This seemed similar to the <a href=\"\/day\/220\">partial views idea<\/a> I explored last year so this article Swizec&#x27;s idea ported to Django with my take on it in a toy project.<\/p>\n<p>The main premise of Swizec&#x27;s article is to have small views and small templates that allow larger views to call the smaller views when constructing the view context. The view chosen for this experiment is a simple dashboard composed of projects and tasks:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token keyword\">def<\/span> <span class=\"token function\">dashboard<\/span><span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    my_tasks <span class=\"token operator\">=<\/span> Task<span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span><span class=\"token builtin\">filter<\/span><span class=\"token punctuation\">(<\/span>assigned_to<span class=\"token operator\">=<\/span>request<span class=\"token punctuation\">.<\/span>user<span class=\"token punctuation\">)<\/span>\n    my_projects <span class=\"token operator\">=<\/span> Project<span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span><span class=\"token builtin\">filter<\/span><span class=\"token punctuation\">(<\/span>created_by<span class=\"token operator\">=<\/span>request<span class=\"token punctuation\">.<\/span>user<span class=\"token punctuation\">)<\/span>\n    todays_tasks <span class=\"token operator\">=<\/span> Task<span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span><span class=\"token builtin\">filter<\/span><span class=\"token punctuation\">(<\/span>due_date__date<span class=\"token operator\">=<\/span>now<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">.<\/span>date<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">)<\/span>\n    <span class=\"token keyword\">return<\/span> render<span class=\"token punctuation\">(<\/span>\n        request<span class=\"token punctuation\">,<\/span>\n        <span class=\"token string\">&quot;index.html&quot;<\/span><span class=\"token punctuation\">,<\/span>\n        <span class=\"token punctuation\">{<\/span>\n            <span class=\"token string\">&quot;my_tasks&quot;<\/span><span class=\"token punctuation\">:<\/span> my_tasks<span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;my_projects&quot;<\/span><span class=\"token punctuation\">:<\/span> my_projects<span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;todays_tasks&quot;<\/span><span class=\"token punctuation\">:<\/span> todays_tasks<span class=\"token punctuation\">,<\/span>\n        <span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>This got split into four separate views as follows:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token keyword\">def<\/span> <span class=\"token function\">my_tasks_view<\/span><span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    my_tasks <span class=\"token operator\">=<\/span> Task<span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span><span class=\"token builtin\">filter<\/span><span class=\"token punctuation\">(<\/span>assigned_to<span class=\"token operator\">=<\/span>request<span class=\"token punctuation\">.<\/span>user<span class=\"token punctuation\">)<\/span>\n    <span class=\"token keyword\">return<\/span> sub_render<span class=\"token punctuation\">(<\/span>\n        request<span class=\"token punctuation\">,<\/span>\n        <span class=\"token string\">&quot;my_tasks.html&quot;<\/span><span class=\"token punctuation\">,<\/span>\n        <span class=\"token punctuation\">{<\/span>\n            <span class=\"token string\">&quot;my_tasks&quot;<\/span><span class=\"token punctuation\">:<\/span> my_tasks<span class=\"token punctuation\">,<\/span>\n        <span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token punctuation\">)<\/span>\n\n<span class=\"token keyword\">def<\/span> <span class=\"token function\">my_projects_view<\/span><span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    my_projects <span class=\"token operator\">=<\/span> Project<span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span><span class=\"token builtin\">filter<\/span><span class=\"token punctuation\">(<\/span>created_by<span class=\"token operator\">=<\/span>request<span class=\"token punctuation\">.<\/span>user<span class=\"token punctuation\">)<\/span>\n    <span class=\"token keyword\">return<\/span> sub_render<span class=\"token punctuation\">(<\/span>\n        request<span class=\"token punctuation\">,<\/span>\n        <span class=\"token string\">&quot;my_projects.html&quot;<\/span><span class=\"token punctuation\">,<\/span>\n        <span class=\"token punctuation\">{<\/span>\n            <span class=\"token string\">&quot;my_projects&quot;<\/span><span class=\"token punctuation\">:<\/span> my_projects<span class=\"token punctuation\">,<\/span>\n        <span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token punctuation\">)<\/span>\n\n<span class=\"token keyword\">def<\/span> <span class=\"token function\">today_tasks<\/span><span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    todays_tasks <span class=\"token operator\">=<\/span> Task<span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span><span class=\"token builtin\">filter<\/span><span class=\"token punctuation\">(<\/span>due_date__date<span class=\"token operator\">=<\/span>now<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">.<\/span>date<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">)<\/span>\n    <span class=\"token keyword\">return<\/span> sub_render<span class=\"token punctuation\">(<\/span>\n        request<span class=\"token punctuation\">,<\/span>\n        <span class=\"token string\">&quot;today.html&quot;<\/span><span class=\"token punctuation\">,<\/span>\n        <span class=\"token punctuation\">{<\/span>\n            <span class=\"token string\">&quot;todays_tasks&quot;<\/span><span class=\"token punctuation\">:<\/span> todays_tasks<span class=\"token punctuation\">,<\/span>\n        <span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token punctuation\">)<\/span>\n\n<span class=\"token keyword\">def<\/span> <span class=\"token function\">dashboard<\/span><span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    <span class=\"token keyword\">return<\/span> render<span class=\"token punctuation\">(<\/span>\n        request<span class=\"token punctuation\">,<\/span>\n        <span class=\"token string\">&quot;dashboard.html&quot;<\/span><span class=\"token punctuation\">,<\/span>\n        <span class=\"token punctuation\">{<\/span>\n            <span class=\"token string\">&quot;my_tasks&quot;<\/span><span class=\"token punctuation\">:<\/span> sub_view<span class=\"token punctuation\">(<\/span>my_tasks_view<span class=\"token punctuation\">,<\/span> request<span class=\"token punctuation\">,<\/span> <span class=\"token string\">&quot;main&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;my_projects&quot;<\/span><span class=\"token punctuation\">:<\/span> sub_view<span class=\"token punctuation\">(<\/span>my_projects_view<span class=\"token punctuation\">,<\/span> request<span class=\"token punctuation\">,<\/span> <span class=\"token string\">&quot;main&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;todays_tasks&quot;<\/span><span class=\"token punctuation\">:<\/span> sub_view<span class=\"token punctuation\">(<\/span>today_tasks<span class=\"token punctuation\">,<\/span> request<span class=\"token punctuation\">,<\/span> <span class=\"token string\">&quot;main&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n        <span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>The split here ought to be fairly obvious in comparison to the single view above. We have introduced 2 shortcut functions <code>sub_view<\/code> and <code>sub_render<\/code> which work together. <code>sub_view<\/code> renders the smaller view and adds an atrribute to the request for template-partials to use. <code>sub_render<\/code> decides if a full render or partial render should be done by checking if the attribute on request exists and then calls the normal render method, code is below!<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token keyword\">def<\/span> <span class=\"token function\">sub_view<\/span><span class=\"token punctuation\">(<\/span>view<span class=\"token punctuation\">,<\/span> request<span class=\"token punctuation\">,<\/span> <span class=\"token builtin\">hash<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    request<span class=\"token punctuation\">.<\/span><span class=\"token builtin\">hash<\/span> <span class=\"token operator\">=<\/span> <span class=\"token builtin\">hash<\/span>\n    <span class=\"token keyword\">return<\/span> view<span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">.<\/span>content<span class=\"token punctuation\">.<\/span>replace<span class=\"token punctuation\">(<\/span><span class=\"token string\">b&quot;\\n&quot;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token string\">b&quot;&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">.<\/span>decode<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;utf-8&quot;<\/span><span class=\"token punctuation\">)<\/span>\n\n\n<span class=\"token keyword\">def<\/span> <span class=\"token function\">sub_render<\/span><span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">,<\/span> template_name<span class=\"token punctuation\">,<\/span> <span class=\"token operator\">*<\/span>args<span class=\"token punctuation\">,<\/span> <span class=\"token operator\">**<\/span>kwargs<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    <span class=\"token keyword\">if<\/span> <span class=\"token builtin\">hasattr<\/span><span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">,<\/span> <span class=\"token string\">&quot;hash&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        template_name <span class=\"token operator\">=<\/span> <span class=\"token string-interpolation\"><span class=\"token string\">f&quot;<\/span><span class=\"token interpolation\"><span class=\"token punctuation\">{<\/span>template_name<span class=\"token punctuation\">}<\/span><\/span><span class=\"token string\">#<\/span><span class=\"token interpolation\"><span class=\"token punctuation\">{<\/span>request<span class=\"token punctuation\">.<\/span><span class=\"token builtin\">hash<\/span><span class=\"token punctuation\">}<\/span><\/span><span class=\"token string\">&quot;<\/span><\/span>\n    <span class=\"token keyword\">return<\/span> render<span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">,<\/span> template_name<span class=\"token punctuation\">,<\/span> <span class=\"token operator\">*<\/span>args<span class=\"token punctuation\">,<\/span> <span class=\"token operator\">**<\/span>kwargs<span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>The final piece of the puzzle is the templates. A sub_view template looks like this:<\/p>\n<pre class=\"language-html\"><code class=\"language-html\">{% extends &quot;base.html&quot; %}\n{% block page_title %}My Projects{% endblock page_title %}\n{% load partials %}\n\n{% block main_content %}\n\n    {% partialdef main %}\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>h2<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>My Projects<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>h2<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dl<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n        {% for project in my_projects %}\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>a<\/span> <span class=\"token attr-name\">href<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>{% url <span class=\"token punctuation\">&#x27;<\/span>project_detail<span class=\"token punctuation\">&#x27;<\/span> project.pk %}<span class=\"token punctuation\">&quot;<\/span><\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ project.name }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>a<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ project.description }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n        {% endfor %}\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dl<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n\n    {% endpartialdef %}\n    {% partial main %}\n{% endblock main_content %}\n<\/code><\/pre>\n<p>and now the main dashboard view looks like this:<\/p>\n<pre class=\"language-html\"><code class=\"language-html\">{% extends &quot;base.html&quot; %}\n{% block page_title %}Dashboard{% endblock page_title %}\n\n{% block main_content %}\n    {{my_projects|safe}}\n\n    {{todays_tasks|safe}}\n\n    {{my_tasks|safe}}\n{% endblock main_content %}\n<\/code><\/pre>\n<p>Here you can see we are leveraging template-partials to decide what to render in our dashboard view or the whole template when the smaller views get rendered normally.<\/p>\n<p>I&#x27;m quite happy with the result of this experiment, with maybe the only improvement is some better syntatic sugar with the shortcut functions, (decorators perhaps?) and perhaps a templatetag to combine the block defintion and partial defintion, say <code>partialblock<\/code> which would allow the definition of a partial and then render it and maybe allow child templates to override it... Perhaps that goes to far though with too much functionality in one tag.<\/p>\n<p>I think this is a pattern I will be trying out in my other projects though to see how far it could be pushed, especially with css container queries allowing for flexible layouts<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-274","title":"Continued adventures into view patterns","summary":"Composite views and partial templates","date_modified":"2025-04-22T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#views","#composite_views","#partial_templates"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-273","content_html":"<p>Another quick tip today, this time with Django-allauth. When using the <code>socialaccounts<\/code> app from Django allauth, there is a templatetag which generates a URL to start the social login process as either a login for a new account or connecting to an existing account. The docs advertise it like this:<\/p>\n<pre class=\"language-html\"><code class=\"language-html\">{% load socialaccount %}\n\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>a<\/span> <span class=\"token attr-name\">href<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>{% provider_login_url <span class=\"token punctuation\">&quot;<\/span><\/span><span class=\"token attr-name\">openid&quot;<\/span> <span class=\"token attr-name\">openid<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>https:\/\/www.google.com\/accounts\/o8\/id<span class=\"token punctuation\">&quot;<\/span><\/span> <span class=\"token attr-name\">next<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>\/success\/url\/<span class=\"token punctuation\">&quot;<\/span><\/span> <span class=\"token attr-name\">%}&quot;<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Google<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>a<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>a<\/span> <span class=\"token attr-name\">href<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>{% provider_login_url <span class=\"token punctuation\">&quot;<\/span><\/span><span class=\"token attr-name\">twitter&quot;<\/span> <span class=\"token attr-name\">%}&quot;<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Twitter<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>a<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n\n<\/code><\/pre>\n<p>This usage of it, adds an extra page which is from allauth which requires styling and design. However I typically don&#x27;t ever want my user to land on this page. Fortunately you can skip the page entirely by using the templatetag in a form instead of an anchor tag. Like so:<\/p>\n<pre class=\"language-html\"><code class=\"language-html\">{% load socialaccount %}\n\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>form<\/span> <span class=\"token attr-name\">action<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>{% provider_login_url <span class=\"token punctuation\">&quot;<\/span><\/span><span class=\"token attr-name\">openid&quot;<\/span> <span class=\"token attr-name\">openid<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>https:\/\/www.google.com\/accounts\/o8\/id<span class=\"token punctuation\">&quot;<\/span><\/span> <span class=\"token attr-name\">next<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>\/success\/url\/<span class=\"token punctuation\">&quot;<\/span><\/span> <span class=\"token attr-name\">%}&quot;<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    {% csrf_token %}\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>button<\/span> <span class=\"token attr-name\">type<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>submit<span class=\"token punctuation\">&quot;<\/span><\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Google<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>button<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>form<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>form<\/span> <span class=\"token attr-name\">action<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>{% provider_login_url <span class=\"token punctuation\">&quot;<\/span><\/span><span class=\"token attr-name\">twitter&quot;<\/span> <span class=\"token attr-name\">%}&quot;<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    {% csrf_token %}\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>button<\/span> <span class=\"token attr-name\">type<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>submit<span class=\"token punctuation\">&quot;<\/span><\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Twitter<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>button<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>form<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n<\/code><\/pre>\n<p>Now these small forms will jump your user straight to the Social Login of your choice!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-273","title":"Django Allauth quick tip","summary":"how to skip an unneccessary page and click","date_modified":"2025-04-14T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#allauth"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-272","content_html":"<p>A quick one today, that&#x27;s more for my future reference, but it might help you! The DisallowedHost error is common in Django especially when standing up a new project. <code>ALLOWED_HOSTS<\/code> needs to be configured with valid hostnames for your application to work. This is good security practice as plenty of bots will hit your site when it goes live, and these errors typically end up in Sentry or some similar tool.<\/p>\n<p>However, Sentry has limits and a log error like this can easily cause your monthly quota to explode (as it did last month for me), which prevents Sentry capturing actual bugs! There are a number of potential fixes to this:<\/p>\n<ol>\n<li>Configure LOGGING to not log the message (I tried and failed)<\/li>\n<li>Add some configuration to the webserver in front of Django (I wanted to minimise other changes and I don&#x27;t care right now about the actual requests hitting Django, just the log message)<\/li>\n<li>Use a function from the Sentry SDK - This worked!<\/li>\n<\/ol>\n<p>The code to ignore the log message in Sentry is as follows:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token keyword\">from<\/span> sentry_sdk<span class=\"token punctuation\">.<\/span>integrations<span class=\"token punctuation\">.<\/span>logging <span class=\"token keyword\">import<\/span> ignore_logger\nignore_logger<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;django.security.DisallowedHost&quot;<\/span><span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>That said I would be interested to know what is missing from this <code>LOGGING<\/code> configuration as this worked locally, but not when deployed. Please do let me know!<\/p>\n<pre class=\"language-py\"><code class=\"language-py\">LOGGING <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">{<\/span>\n    <span class=\"token string\">&quot;version&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token number\">1<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token string\">&quot;disable_existing_loggers&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token boolean\">True<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token string\">&quot;formatters&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token punctuation\">{<\/span>\n        <span class=\"token string\">&quot;verbose&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token punctuation\">{<\/span>\n            <span class=\"token string\">&quot;format&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">&quot;%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s&quot;<\/span><span class=\"token punctuation\">,<\/span>\n        <span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token string\">&quot;handlers&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token punctuation\">{<\/span>\n        <span class=\"token string\">&quot;console&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token punctuation\">{<\/span>\n            <span class=\"token string\">&quot;level&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">&quot;DEBUG&quot;<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;class&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">&quot;logging.StreamHandler&quot;<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;formatter&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">&quot;verbose&quot;<\/span><span class=\"token punctuation\">,<\/span>\n        <span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">,<\/span>\n        <span class=\"token string\">&quot;null&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token punctuation\">{<\/span>\n            <span class=\"token string\">&quot;level&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">&quot;DEBUG&quot;<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;class&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">&quot;logging.NullHandler&quot;<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;formatter&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">&quot;verbose&quot;<\/span><span class=\"token punctuation\">,<\/span>\n        <span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token string\">&quot;root&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token punctuation\">{<\/span><span class=\"token string\">&quot;level&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">&quot;INFO&quot;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token string\">&quot;handlers&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token punctuation\">[<\/span><span class=\"token string\">&quot;console&quot;<\/span><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token string\">&quot;loggers&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token punctuation\">{<\/span>\n        <span class=\"token string\">&quot;django.db.backends&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token punctuation\">{<\/span>\n            <span class=\"token string\">&quot;level&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">&quot;ERROR&quot;<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;handlers&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token punctuation\">[<\/span><span class=\"token string\">&quot;console&quot;<\/span><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;propagate&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token boolean\">False<\/span><span class=\"token punctuation\">,<\/span>\n        <span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">,<\/span>\n        <span class=\"token comment\"># Errors logged by the SDK itself<\/span>\n        <span class=\"token string\">&quot;sentry_sdk&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token punctuation\">{<\/span><span class=\"token string\">&quot;level&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">&quot;ERROR&quot;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token string\">&quot;handlers&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token punctuation\">[<\/span><span class=\"token string\">&quot;console&quot;<\/span><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token string\">&quot;propagate&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token boolean\">False<\/span><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">,<\/span>\n        <span class=\"token string\">&quot;django.security.DisallowedHost&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token punctuation\">{<\/span>\n            <span class=\"token string\">&quot;level&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">&quot;ERROR&quot;<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;handlers&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token punctuation\">[<\/span><span class=\"token string\">&quot;null&quot;<\/span><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;propagate&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token boolean\">False<\/span><span class=\"token punctuation\">,<\/span>\n        <span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">,<\/span>\n<span class=\"token punctuation\">}<\/span>\n<\/code><\/pre>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-272","title":"Silencing Disallowed Host Errors in Django","summary":"...so they do not consume your whole Sentry quota","date_modified":"2025-04-03T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#disallowed_host","#sentry"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-271","content_html":"<p>This post was triggered by reading a recent proposal in the Django forum. Part of the proposal suggested the implementation of a new setting, which as I understand it is currently not a desired solution with in the community. I have previously read that the consensus is that there are too many settings and the last several releases have had contributions combining multiple settings into a larger single setting with a more complex structure.<\/p>\n<p>Pondering on that specific situation led me to compare how I deal with long term technical debt in the codebases I manage. The process is typically a 4-step process which is defined as follows:<\/p>\n<ol>\n<li>Define the new vision or direction of travel (eg less settings)<\/li>\n<li>Some initial work to introduce the new direction<\/li>\n<li>A long process of replicating the new behaviour\/code across the codebase.<\/li>\n<li>A final clean up of the old code or associated flags.<\/li>\n<\/ol>\n<p>While that process could define most software development, I find it more true when dealing with technical debt. This has led me to wonder what other trends are we passively addressing as a community? These are the things that are likely involved whether a proposal gets enough plus one&#x27;s, minus one&#x27;s or shape any eventual code that makes it into Django itself. What&#x27;s interesting to me is that these trends are unlikely to be documented in Django as tasks because they are soft arguments or if they are they will be within the contributing documenation.<\/p>\n<p>Finally I started a <a href=\"https:\/\/forum.djangoproject.com\/t\/documenting-existing-prevailing-consensus-trends-within-the-community\/39906\/\">forum post<\/a> asking the community for more of these trends in the hope that we could eventually get them documented. This also allows us as a community to periodically review and challenge if they are still valid.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-271","title":"Documenting Technical Debt in Django","summary":"Are there some trends that we need to be more explicit about?","date_modified":"2025-03-28T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#community","#contributing","#technical_debt"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-270","content_html":"<p>The problem before me last week was to build out the navigation for a section of the new startup application. I was about to install a template component library (django-bird was the choice...and I still might!), when I noticed in an issue on django-bird that coming in Django 5.2 is the <code>simple_block_tag<\/code> template tag. After reviewing the example in the <a href=\"https:\/\/docs.djangoproject.com\/en\/dev\/howto\/custom-template-tags\/#a-complete-example\">docs<\/a> I thought it would be a perfect fit for my use case. The result was 3 template tags that allow me to build out a navigation that has toggles with sub items and tracking the currently active page.<\/p>\n<p>First here is the final template in which they are used:<\/p>\n<pre class=\"language-django\"><code class=\"language-django\"><span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{%<\/span> <span class=\"token tag keyword\">navitem<\/span> <span class=\"token variable\">url_name<\/span><span class=\"token operator\">=<\/span><span class=\"token string\">&#x27;thing-dashboard&#x27;<\/span> <span class=\"token delimiter punctuation\">%}<\/span><\/span>Dashboard<span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{%<\/span> <span class=\"token tag keyword\">endnavitem<\/span> <span class=\"token delimiter punctuation\">%}<\/span><\/span>\n<span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{%<\/span> <span class=\"token tag keyword\">togglenavitem<\/span> <span class=\"token variable\">toggle_name<\/span><span class=\"token operator\">=<\/span><span class=\"token string\">&quot;THINGS&quot;<\/span> <span class=\"token delimiter punctuation\">%}<\/span><\/span>\n  <span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{%<\/span> <span class=\"token tag keyword\">subnavitem<\/span> <span class=\"token variable\">url_name<\/span><span class=\"token operator\">=<\/span><span class=\"token string\">&#x27;thing-list&#x27;<\/span> <span class=\"token delimiter punctuation\">%}<\/span><\/span>Overview<span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{%<\/span> <span class=\"token tag keyword\">endsubnavitem<\/span> <span class=\"token delimiter punctuation\">%}<\/span><\/span>\n  <span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{%<\/span> <span class=\"token tag keyword\">for<\/span> <span class=\"token variable\">thin<\/span> <span class=\"token keyword\">in<\/span> <span class=\"token variable\">things<\/span> <span class=\"token delimiter punctuation\">%}<\/span><\/span>\n    <span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{%<\/span> <span class=\"token tag keyword\">subnavitem<\/span> <span class=\"token variable\">url_name<\/span><span class=\"token operator\">=<\/span><span class=\"token variable\">account<\/span><span class=\"token punctuation\">.<\/span><span class=\"token variable\">get_absolute_url<\/span> <span class=\"token delimiter punctuation\">%}<\/span><\/span>Thing <span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{{<\/span> <span class=\"token variable\">thin<\/span><span class=\"token punctuation\">.<\/span><span class=\"token variable\">pk<\/span> <span class=\"token delimiter punctuation\">}}<\/span><\/span><span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{%<\/span> <span class=\"token tag keyword\">endsubnavitem<\/span> <span class=\"token delimiter punctuation\">%}<\/span><\/span>\n  <span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{%<\/span> <span class=\"token tag keyword\">endfor<\/span> <span class=\"token delimiter punctuation\">%}<\/span><\/span>\n  <span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{%<\/span> <span class=\"token tag keyword\">subnavitem<\/span> <span class=\"token variable\">url_name<\/span><span class=\"token operator\">=<\/span><span class=\"token string\">&#x27;thing-create&#x27;<\/span> <span class=\"token delimiter punctuation\">%}<\/span><\/span>\n  New thing\n  <span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{%<\/span> <span class=\"token tag keyword\">endsubnavitem<\/span> <span class=\"token delimiter punctuation\">%}<\/span><\/span>\n<span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{%<\/span> <span class=\"token tag keyword\">endtogglenavitem<\/span> <span class=\"token delimiter punctuation\">%}<\/span><\/span>\n<\/code><\/pre>\n<p>This has a top-level navigation item and a toggle which lists a list of &quot;things&quot; with option of creating a new thing at the end. Let&#x27;s start simple with the main nav item. The template tag is as follows:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token decorator annotation punctuation\">@register<span class=\"token punctuation\">.<\/span>simple_block_tag<\/span><span class=\"token punctuation\">(<\/span>takes_context<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">)<\/span>\n<span class=\"token keyword\">def<\/span> <span class=\"token function\">navitem<\/span><span class=\"token punctuation\">(<\/span>context<span class=\"token punctuation\">,<\/span> content<span class=\"token punctuation\">,<\/span> url_name<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    url <span class=\"token operator\">=<\/span> resolve_url<span class=\"token punctuation\">(<\/span>url_name<span class=\"token punctuation\">)<\/span>\n    is_active <span class=\"token operator\">=<\/span> context<span class=\"token punctuation\">[<\/span><span class=\"token string\">&#x27;request&#x27;<\/span><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">.<\/span>path <span class=\"token operator\">==<\/span> url\n    format_kwargs <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">{<\/span>\n        <span class=\"token string\">&quot;content&quot;<\/span><span class=\"token punctuation\">:<\/span> content<span class=\"token punctuation\">,<\/span>\n        <span class=\"token string\">&quot;url&quot;<\/span><span class=\"token punctuation\">:<\/span> url<span class=\"token punctuation\">,<\/span>\n        <span class=\"token string\">&quot;is_active_classes&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">&quot;bg-blue-50&quot;<\/span> <span class=\"token keyword\">if<\/span> is_active <span class=\"token keyword\">else<\/span> <span class=\"token string\">&quot;bg-gray-50 hover:bg-blue-50&quot;<\/span>\n    <span class=\"token punctuation\">}<\/span>\n    <span class=\"token keyword\">return<\/span> get_template<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;nav\/item.html&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">.<\/span>render<span class=\"token punctuation\">(<\/span>format_kwargs<span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>and the referenced template being:<\/p>\n<pre class=\"language-django\"><code class=\"language-django\">  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>li<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>a<\/span> <span class=\"token attr-name\">href<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span><span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{{<\/span><span class=\"token variable\">url<\/span><span class=\"token delimiter punctuation\">}}<\/span><\/span><span class=\"token punctuation\">&quot;<\/span><\/span>\n       <span class=\"token attr-name\">class<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>block rounded-md py-2 pl-10 pr-2 text-sm\/6 font-semibold text-gray-700 <span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{{<\/span><span class=\"token variable\">is_active_classes<\/span><span class=\"token delimiter punctuation\">}}<\/span><\/span><span class=\"token punctuation\">&quot;<\/span><\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{{<\/span><span class=\"token variable\">content<\/span><span class=\"token delimiter punctuation\">}}<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>a<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>li<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n<\/code><\/pre>\n<p>It&#x27;s really quite simple, almost like a sub-view? We simple construct the necessary variables for the template and then render it! The other two template tags are very similar as shown below for completeness.<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token decorator annotation punctuation\">@register<span class=\"token punctuation\">.<\/span>simple_block_tag<\/span><span class=\"token punctuation\">(<\/span>takes_context<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">)<\/span>\n<span class=\"token keyword\">def<\/span> <span class=\"token function\">subnavitem<\/span><span class=\"token punctuation\">(<\/span>context<span class=\"token punctuation\">,<\/span> content<span class=\"token punctuation\">,<\/span> url_name<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    url <span class=\"token operator\">=<\/span> resolve_url<span class=\"token punctuation\">(<\/span>url_name<span class=\"token punctuation\">)<\/span>\n    is_active <span class=\"token operator\">=<\/span> context<span class=\"token punctuation\">[<\/span><span class=\"token string\">&#x27;request&#x27;<\/span><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">.<\/span>path <span class=\"token operator\">==<\/span> url\n    format_kwargs <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">{<\/span>\n        <span class=\"token string\">&quot;content&quot;<\/span><span class=\"token punctuation\">:<\/span> content<span class=\"token punctuation\">,<\/span>\n        <span class=\"token string\">&quot;url&quot;<\/span><span class=\"token punctuation\">:<\/span> url<span class=\"token punctuation\">,<\/span>\n        <span class=\"token string\">&quot;is_active_classes&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">&quot;bg-blue-50 is_active&quot;<\/span> <span class=\"token keyword\">if<\/span> is_active <span class=\"token keyword\">else<\/span> <span class=\"token string\">&quot;bg-gray-50 hover:bg-blue-50&quot;<\/span>\n    <span class=\"token punctuation\">}<\/span>\n    <span class=\"token keyword\">return<\/span> get_template<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;nav\/subitem.html&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">.<\/span>render<span class=\"token punctuation\">(<\/span>format_kwargs<span class=\"token punctuation\">)<\/span>\n\n\n<span class=\"token decorator annotation punctuation\">@register<span class=\"token punctuation\">.<\/span>simple_block_tag<\/span><span class=\"token punctuation\">(<\/span>takes_context<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">)<\/span>\n<span class=\"token keyword\">def<\/span> <span class=\"token function\">togglenavitem<\/span><span class=\"token punctuation\">(<\/span>context<span class=\"token punctuation\">,<\/span> content<span class=\"token punctuation\">,<\/span> toggle_name<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    format_kwargs <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">{<\/span>\n        <span class=\"token string\">&quot;content&quot;<\/span><span class=\"token punctuation\">:<\/span> content<span class=\"token punctuation\">,<\/span>\n        <span class=\"token string\">&quot;toggle_name&quot;<\/span><span class=\"token punctuation\">:<\/span> toggle_name<span class=\"token punctuation\">,<\/span>\n        <span class=\"token string\">&quot;toggle_checked&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">&quot;checked&quot;<\/span> <span class=\"token keyword\">if<\/span> <span class=\"token string\">&#x27;is_active&#x27;<\/span> <span class=\"token keyword\">in<\/span> content <span class=\"token keyword\">else<\/span> <span class=\"token string\">&quot;&quot;<\/span>\n    <span class=\"token punctuation\">}<\/span>\n    <span class=\"token keyword\">return<\/span> get_template<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;nav\/togglenavitem.html&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">.<\/span>render<span class=\"token punctuation\">(<\/span>format_kwargs<span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>The subnavitem template:<\/p>\n<pre class=\"language-django\"><code class=\"language-django\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>li<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>a<\/span> <span class=\"token attr-name\">href<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span><span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{{<\/span><span class=\"token variable\">url<\/span><span class=\"token delimiter punctuation\">}}<\/span><\/span><span class=\"token punctuation\">&quot;<\/span><\/span>\n      <span class=\"token attr-name\">class<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>block rounded-md py-2 pl-9 pr-2 text-sm\/6 text-gray-700 <span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{{<\/span><span class=\"token variable\">is_active_classes<\/span><span class=\"token delimiter punctuation\">}}<\/span><\/span><span class=\"token punctuation\">&quot;<\/span><\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{{<\/span><span class=\"token variable\">content<\/span><span class=\"token delimiter punctuation\">}}<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>a<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>li<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n<\/code><\/pre>\n<p>The togglenavitem template:<\/p>\n<pre class=\"language-django\"><code class=\"language-django\">    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>li<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n      <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>div<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>input<\/span> <span class=\"token attr-name\">type<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>checkbox<span class=\"token punctuation\">&quot;<\/span><\/span> <span class=\"token attr-name\">id<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>toggle_<span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{{<\/span><span class=\"token variable\">toggle_name<\/span><span class=\"token delimiter punctuation\">}}<\/span><\/span><span class=\"token punctuation\">&quot;<\/span><\/span> <span class=\"token attr-name\">class<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>peer hidden<span class=\"token punctuation\">&quot;<\/span><\/span> <span class=\"token attr-name\"><span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{{<\/span><span class=\"token variable\">toggle_checked<\/span><span class=\"token delimiter punctuation\">}}<\/span><\/span><\/span> <span class=\"token punctuation\">\/&gt;<\/span><\/span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>label<\/span> <span class=\"token attr-name\">for<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>toggle_<span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{{<\/span><span class=\"token variable\">toggle_name<\/span><span class=\"token delimiter punctuation\">}}<\/span><\/span><span class=\"token punctuation\">&quot;<\/span><\/span>\n                <span class=\"token attr-name\">class<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>flex w-full items-center gap-x-3 rounded-md p-2 text-left text-sm\/6 font-semibold text-gray-700 hover:bg-gray-50 peer-checked:[&amp;&gt;svg]:rotate-90 peer-checked:[&amp;&gt;svg]:text-gray-500<span class=\"token punctuation\">&quot;<\/span><\/span>\n                <span class=\"token attr-name\">aria-controls<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>sub-menu-1<span class=\"token punctuation\">&quot;<\/span><\/span>\n                <span class=\"token attr-name\">aria-expanded<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>false<span class=\"token punctuation\">&quot;<\/span><\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n          <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>svg<\/span> <span class=\"token attr-name\">class<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>size-5 shrink-0 text-gray-400 transition-transform duration-200<span class=\"token punctuation\">&quot;<\/span><\/span>\n               <span class=\"token attr-name\">viewBox<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>0 0 20 20<span class=\"token punctuation\">&quot;<\/span><\/span>\n               <span class=\"token attr-name\">fill<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>currentColor<span class=\"token punctuation\">&quot;<\/span><\/span>\n               <span class=\"token attr-name\">aria-hidden<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>true<span class=\"token punctuation\">&quot;<\/span><\/span>\n               <span class=\"token attr-name\">data-slot<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>icon<span class=\"token punctuation\">&quot;<\/span><\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>path<\/span> <span class=\"token attr-name\">fill-rule<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>evenodd<span class=\"token punctuation\">&quot;<\/span><\/span> <span class=\"token attr-name\">d<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>M8.22 5.22a.75.75 0 0 1 1.06 0l4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.75.75 0 0 1-1.06-1.06L11.94 10 8.22 6.28a.75.75 0 0 1 0-1.06Z<span class=\"token punctuation\">&quot;<\/span><\/span> <span class=\"token attr-name\">clip-rule<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>evenodd<span class=\"token punctuation\">&quot;<\/span><\/span> <span class=\"token punctuation\">\/&gt;<\/span><\/span>\n          <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>svg<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n          <span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{{<\/span><span class=\"token variable\">toggle_name<\/span><span class=\"token delimiter punctuation\">}}<\/span><\/span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>label<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>ul<\/span> <span class=\"token attr-name\">class<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>mt-1 px-2 overflow-hidden max-h-0 peer-checked:max-h-96 transition-all duration-200<span class=\"token punctuation\">&quot;<\/span><\/span> <span class=\"token attr-name\">id<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>sub-menu-1<span class=\"token punctuation\">&quot;<\/span><\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token django language-django\"><span class=\"token delimiter punctuation\">{{<\/span><span class=\"token variable\">content<\/span><span class=\"token delimiter punctuation\">}}<\/span><\/span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>ul<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n      <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>div<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>li<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n<\/code><\/pre>\n<p>We use a hidden checkbox to store the state if the toggle is open and the CSS class <code>is_active<\/code> to track if a subnavitem is the active page to keep the relevant toggle open.<\/p>\n<p>Some final thoughts, first thanks to Jake Howard for proposing and implementing this change! Second he has proposed a similar tag for inclusion tags which would reduce the small amount of boilerplate in the template rendering. I do wonder however if it&#x27;s possible to ditch writing any python templatetags at all? Could I specify the formatting in the template itself? That&#x27;s probably where a third-party package comes in for now, which I will be happy to do, but until then I&#x27;m going to see how far I can go with the <code>simple_block_tag<\/code>!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-270","title":"Django Template components are slowly coming","summary":"5.2 brings the Simple Block tag which is very similar to React children","date_modified":"2025-03-20T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#templates","#components"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-269","content_html":"<p>So I have been missed a few weeks of writing and over the weekend was pondering why that has happened. The obvious reason is my switch from daily writing (my goal for last year) to roughly weekly. The key to my failure here is the &#x27;roughly&#x27; part. I have yet to set a specific deadline in a week when a post needs to be published, and so I get to the end of the week without a post. This is all down to a habit, the environment and expectation I set for myself.<\/p>\n<p>But that&#x27;s not all, reflecting back on my time writing daily, my mind was always ticking away at what tomorrow&#x27;s post would be, content was always top of mind and when I had a plan of what to write, that is when the post was easiest to write. Essentially when I had written most of it already in my mind. Now that I&#x27;m writing less often, I&#x27;m not crafting my thoughts in to writing for the next day. This intensity of the consistency actually helped me be consistent compared with a lesser intensity.<\/p>\n<p>Finally, there have been some personal changes which has shortened my workday slightly which puts pressure on me to get the paid work done first, which sometimes results in the nice to have tasks being postponed until another day (or week).<\/p>\n<p>That&#x27;s all great, but how do I resolve this? First is resetting my expectations, so let&#x27;s say that a blog post will be out by the end of the day Thursday and that it&#x27;s going to follow the same format as last year for the time being, that being short snappy posts of ~100 to 200 words. With that said, expect another post this week!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-269","title":"On writing daily compared with weekly","summary":"A retrospective and a commitment","date_modified":"2025-03-13T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#meta","#writing"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-268","content_html":"<p>First go an read Carlton&#x27;s <a href=\"https:\/\/buttondown.com\/carlton\/archive\/managing-djangos-queue\/\">latest post<\/a> if you haven&#x27;t already. Once again it&#x27;s a great thoughtful read, that pushes me to consider the problems we face as a community and triggers ideas of what we could experiment with to improve the community iteractions.<\/p>\n<p>This month was subject of the queue of tickets in Django. I&#x27;m not going to say much more than that. Go read the post! What struck me most when reading the post was the social component present in the problem and in any solution, but that I mean the communication that happens on every side.<\/p>\n<p>Take the drive-by requests, how do we switch that stance to one of giving first over requesting something? There could be some form of limitation before you are allowed to request a new feature directly, say being a DSF individual or corporate member or it requires a member to second a proposal. This to some extent requires quantifying community enagagement because a single level is unlikely to be enough in the long term. To some extent we have started this in the Discord Server, engagement is tracked and roles awarded. Do those levels &amp; roles do anything directly right now? Not really, we certainly look at the levels when considering new helpers, but it could open up new areas of the server. A similar system exists in the Forum, it&#x27;s called Trust Levels there, we could sync these two so an active Discord user doesn&#x27;t need to start from scratch and vice versa.<\/p>\n<p>The leads me on to Github, which again is another area of users, interactions and permissions. It also speaks to Carlton&#x27;s point of modularity, could we raise the profile of the overall Django Github Organisation compared with the single django repo. Having a process for opening up the organisation could be a path forward and a potential middle ground for modules to move out of core or a stepping stone into core. Finally there is also finishing off DEP 2 in this regard, experimental APIs with a potentially quicker release cycle. Is this still too much of a Stadium model? I don&#x27;t know, but I feel it&#x27;s worth a try.<\/p>\n<p>All of the above makes me excited for the future of Django and in particular what the Online Community Working Group can do to enable these experiments and ideas, more on that soon!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-268","title":"Thoughts on the latest The Stack Report","summary":"Reading it always seems to generate an idea or two","date_modified":"2025-02-13T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#contributing","#TheStackReport","#community"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-267","content_html":"<p>Ok, the title was clickbait! Today I properly started working on the new startup. First up is an abstract base model for every other model to inherit. I got to this point:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token keyword\">class<\/span> <span class=\"token class-name\">BaseModel<\/span><span class=\"token punctuation\">(<\/span>models<span class=\"token punctuation\">.<\/span>Model<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n\n    created_at <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>DateTimeField<span class=\"token punctuation\">(<\/span>auto_now_add<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">)<\/span>\n    updated_at <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>DateTimeField<span class=\"token punctuation\">(<\/span>auto_now<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">)<\/span>\n\n    external_id <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>UUIDField<span class=\"token punctuation\">(<\/span>default<span class=\"token operator\">=<\/span>uuid7<span class=\"token punctuation\">,<\/span> editable<span class=\"token operator\">=<\/span><span class=\"token boolean\">False<\/span><span class=\"token punctuation\">,<\/span> unique<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">)<\/span>\n\n    <span class=\"token keyword\">class<\/span> <span class=\"token class-name\">Meta<\/span><span class=\"token punctuation\">(<\/span>TypedModelMeta<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        abstract <span class=\"token operator\">=<\/span> <span class=\"token boolean\">True<\/span>\n\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">get_absolute_url<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        <span class=\"token keyword\">return<\/span> reverse<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>At this point, I thought could I have a detail view for any object in my database (without manually creating a view for it), accessed by <code>get_absolute_url<\/code>. Naturally Neapolitan came to mind and I started to play around with it and landed on this modified version of the CRUDView.<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token keyword\">class<\/span> <span class=\"token class-name\">MyRole<\/span><span class=\"token punctuation\">:<\/span>\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">reverse<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">,<\/span> view<span class=\"token punctuation\">,<\/span> <span class=\"token builtin\">object<\/span><span class=\"token operator\">=<\/span><span class=\"token boolean\">None<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        url_name <span class=\"token operator\">=<\/span> <span class=\"token string-interpolation\"><span class=\"token string\">f&quot;<\/span><span class=\"token interpolation\"><span class=\"token punctuation\">{<\/span>view<span class=\"token punctuation\">.<\/span>get_url_base<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">}<\/span><\/span><span class=\"token string\">-<\/span><span class=\"token interpolation\"><span class=\"token punctuation\">{<\/span>self<span class=\"token punctuation\">.<\/span>url_name_component<span class=\"token punctuation\">}<\/span><\/span><span class=\"token string\">&quot;<\/span><\/span>\n        url_kwarg <span class=\"token operator\">=<\/span> view<span class=\"token punctuation\">.<\/span>lookup_url_kwarg <span class=\"token keyword\">or<\/span> view<span class=\"token punctuation\">.<\/span>lookup_field\n        <span class=\"token keyword\">match<\/span> self<span class=\"token punctuation\">:<\/span>\n            <span class=\"token keyword\">case<\/span> Role<span class=\"token punctuation\">.<\/span>LIST <span class=\"token operator\">|<\/span> Role<span class=\"token punctuation\">.<\/span>CREATE<span class=\"token punctuation\">:<\/span>\n                <span class=\"token keyword\">return<\/span> reverse<span class=\"token punctuation\">(<\/span>url_name<span class=\"token punctuation\">)<\/span>\n            <span class=\"token keyword\">case<\/span> <span class=\"token keyword\">_<\/span><span class=\"token punctuation\">:<\/span>\n                <span class=\"token keyword\">return<\/span> reverse<span class=\"token punctuation\">(<\/span>\n                    url_name<span class=\"token punctuation\">,<\/span>\n                    kwargs<span class=\"token operator\">=<\/span><span class=\"token punctuation\">{<\/span>url_kwarg<span class=\"token punctuation\">:<\/span> <span class=\"token builtin\">getattr<\/span><span class=\"token punctuation\">(<\/span><span class=\"token builtin\">object<\/span><span class=\"token punctuation\">,<\/span> view<span class=\"token punctuation\">.<\/span>lookup_field<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">,<\/span>\n                <span class=\"token punctuation\">)<\/span>\n\nRole<span class=\"token punctuation\">.<\/span>reverse <span class=\"token operator\">=<\/span> MyRole<span class=\"token punctuation\">.<\/span>reverse\n\n<span class=\"token keyword\">class<\/span> <span class=\"token class-name\">GenericCRUDView<\/span><span class=\"token punctuation\">(<\/span>CRUDView<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    lookup_field <span class=\"token operator\">=<\/span> <span class=\"token string\">&#x27;pk&#x27;<\/span>\n\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">setup<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">,<\/span> request<span class=\"token punctuation\">,<\/span> <span class=\"token operator\">*<\/span>args<span class=\"token punctuation\">,<\/span> <span class=\"token operator\">**<\/span>kwargs<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        <span class=\"token builtin\">super<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">.<\/span>setup<span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">,<\/span> <span class=\"token operator\">*<\/span>args<span class=\"token punctuation\">,<\/span> <span class=\"token operator\">**<\/span>kwargs<span class=\"token punctuation\">)<\/span>\n        app<span class=\"token punctuation\">,<\/span> model <span class=\"token operator\">=<\/span> self<span class=\"token punctuation\">.<\/span>request<span class=\"token punctuation\">.<\/span>path<span class=\"token punctuation\">.<\/span>split<span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;\/&#x27;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">[<\/span><span class=\"token number\">1<\/span><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">.<\/span>split<span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;-&#x27;<\/span><span class=\"token punctuation\">)<\/span>\n        self<span class=\"token punctuation\">.<\/span>model <span class=\"token operator\">=<\/span> apps<span class=\"token punctuation\">.<\/span>get_model<span class=\"token punctuation\">(<\/span><span class=\"token string-interpolation\"><span class=\"token string\">f&quot;<\/span><span class=\"token interpolation\"><span class=\"token punctuation\">{<\/span>app<span class=\"token punctuation\">}<\/span><\/span><span class=\"token string\">.<\/span><span class=\"token interpolation\"><span class=\"token punctuation\">{<\/span>model<span class=\"token punctuation\">}<\/span><\/span><span class=\"token string\">&quot;<\/span><\/span><span class=\"token punctuation\">)<\/span>\n        self<span class=\"token punctuation\">.<\/span>fields <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">[<\/span>field<span class=\"token punctuation\">.<\/span>name <span class=\"token keyword\">for<\/span> field <span class=\"token keyword\">in<\/span> self<span class=\"token punctuation\">.<\/span>model<span class=\"token punctuation\">.<\/span>_meta<span class=\"token punctuation\">.<\/span>fields<span class=\"token punctuation\">]<\/span>\n\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">get_url_base<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        <span class=\"token keyword\">return<\/span> <span class=\"token string-interpolation\"><span class=\"token string\">f&#x27;<\/span><span class=\"token interpolation\"><span class=\"token punctuation\">{<\/span>self<span class=\"token punctuation\">.<\/span>model<span class=\"token punctuation\">.<\/span>_meta<span class=\"token punctuation\">.<\/span>app_label<span class=\"token punctuation\">}<\/span><\/span><span class=\"token string\">-<\/span><span class=\"token interpolation\"><span class=\"token punctuation\">{<\/span>self<span class=\"token punctuation\">.<\/span>model<span class=\"token punctuation\">.<\/span>_meta<span class=\"token punctuation\">.<\/span>model_name<span class=\"token punctuation\">}<\/span><\/span><span class=\"token string\">&#x27;<\/span><\/span>\n\n    <span class=\"token decorator annotation punctuation\">@classproperty<\/span>\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">url_base<\/span><span class=\"token punctuation\">(<\/span>cls<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        <span class=\"token keyword\">return<\/span> <span class=\"token string-interpolation\"><span class=\"token string\">f&#x27;<\/span><span class=\"token interpolation\"><span class=\"token punctuation\">{<\/span>cls<span class=\"token punctuation\">.<\/span>model<span class=\"token punctuation\">.<\/span>_meta<span class=\"token punctuation\">.<\/span>app_label<span class=\"token punctuation\">}<\/span><\/span><span class=\"token string\">-<\/span><span class=\"token interpolation\"><span class=\"token punctuation\">{<\/span>cls<span class=\"token punctuation\">.<\/span>model<span class=\"token punctuation\">.<\/span>_meta<span class=\"token punctuation\">.<\/span>model_name<span class=\"token punctuation\">}<\/span><\/span><span class=\"token string\">&#x27;<\/span><\/span>\n\n    <span class=\"token decorator annotation punctuation\">@classmethod<\/span>\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">get_all_urls<\/span><span class=\"token punctuation\">(<\/span>cls<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        urls <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">[<\/span><span class=\"token punctuation\">]<\/span>\n        <span class=\"token keyword\">for<\/span> model <span class=\"token keyword\">in<\/span> apps<span class=\"token punctuation\">.<\/span>get_models<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n            GenericCRUDView<span class=\"token punctuation\">.<\/span>model <span class=\"token operator\">=<\/span> model\n            urls <span class=\"token operator\">+=<\/span> GenericCRUDView<span class=\"token punctuation\">.<\/span>get_urls<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n        <span class=\"token keyword\">return<\/span> urls\n<\/code><\/pre>\n<p>First up I added the classmethod of <code>get_all_urls<\/code> to go through each model and generate the urls required. Next the modification to <code>setup<\/code> is to dynamically set the model and fields based on the path from the browser which is <code>&lt;app_label&gt;-&lt;model_name&gt;<\/code>. This is in combination with overriding <code>url_base<\/code> to match.<\/p>\n<p>This worked, except that the links generated only match the last model set as the class attribute in <code>get_all_models<\/code>. To fix this I had to monkey patch the <code>reverse<\/code> in the <code>Role<\/code> class to switch <code>view.url_base<\/code> for <code>view.get_url_base<\/code> which are the same except for one is the class and one is for the instance.<\/p>\n<p>The main thing on the surface that is missing is an index page to list each of the models and we are close to the beginnings of an admin. As for me I will be content with having a single <code>get_absolute_url<\/code> method in my codebase, at least to begin with!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-267","title":"Is this the beginning of a new Django admin...","summary":"Hacking on Neapolitan for some quick wins","date_modified":"2025-02-07T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#admin","#quick_wins"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-266","content_html":"<p>Last year while working for a short-term client, I came across a Javascript API called <code>MutationObserver<\/code>. It&#x27;s a handy bit of kit that allows an application to watch for changes on part of the DOM (or all of it), then it you can specify a callback to do what you require. This is probably most relevant in browser extensions or if your app ships a Javascript snippet to other sites or if you want to hook into a third-party snippet in some way.<\/p>\n<p>Comfort Monitor Live is a browser extension that currently rearranges a specific page by rewriting the CSS of the page dynamically. The layout designer that I&#x27;m shipping would have increased that complexity if I hadn&#x27;t come across this API. Currently the extension moves the existing elements around, that being more flexible would have led to a mess!<\/p>\n<p>Instead I hide the existing content, use a MutationObserver to watch for changes to the original content, then inject my own HTML which I have full control over to arrange how I like! This allows for much cleaner code that will be easier to maintain.<\/p>\n<p>So what&#x27;s the code for a MutationObserver I hear you ask? Well it&#x27;s fairly simple, see below for an example which just logs the mutations to console.<\/p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">const<\/span> main <span class=\"token operator\">=<\/span> <span class=\"token dom variable\">document<\/span><span class=\"token punctuation\">.<\/span><span class=\"token method function property-access\">querySelector<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;main&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n<span class=\"token comment\">\/\/ This object below specifies what changes to look for.<\/span>\n<span class=\"token comment\">\/\/ This config picks up text changes to all children of the main tag (so basically the whole page)<\/span>\n<span class=\"token keyword\">const<\/span> config <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">{<\/span> <span class=\"token literal-property property\">attributes<\/span><span class=\"token operator\">:<\/span> <span class=\"token boolean\">false<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token literal-property property\">childList<\/span><span class=\"token operator\">:<\/span> <span class=\"token boolean\">true<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token literal-property property\">characterData<\/span><span class=\"token operator\">:<\/span> <span class=\"token boolean\">true<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token literal-property property\">subtree<\/span><span class=\"token operator\">:<\/span> <span class=\"token boolean\">true<\/span> <span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">;<\/span>\n<span class=\"token keyword\">const<\/span> <span class=\"token function-variable function\">callback<\/span> <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token parameter\">mutationList<span class=\"token punctuation\">,<\/span> observer<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token arrow operator\">=&gt;<\/span> <span class=\"token punctuation\">{<\/span>\n  <span class=\"token keyword control-flow\">for<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token keyword\">const<\/span> mutation <span class=\"token keyword\">of<\/span> mutationList<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span>\n    <span class=\"token console class-name\">console<\/span><span class=\"token punctuation\">.<\/span><span class=\"token method function property-access\">log<\/span><span class=\"token punctuation\">(<\/span>mutation<span class=\"token punctuation\">)<\/span>\n  <span class=\"token punctuation\">}<\/span>\n<span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">;<\/span>\n\n<span class=\"token keyword\">const<\/span> observer <span class=\"token operator\">=<\/span> <span class=\"token keyword\">new<\/span> <span class=\"token class-name\">MutationObserver<\/span><span class=\"token punctuation\">(<\/span>callback<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n<span class=\"token comment\">\/\/ Start observing the target node for configured mutations<\/span>\nobserver<span class=\"token punctuation\">.<\/span><span class=\"token method function property-access\">observe<\/span><span class=\"token punctuation\">(<\/span>main<span class=\"token punctuation\">,<\/span> config<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n<\/code><\/pre>\n<p>That&#x27;s it! Fairly simple.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-266","title":"Watching for changes on a web page you don't control","summary":"The second discovery for improving Comfort Monitor Live","date_modified":"2025-01-31T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#js","#side_project","#mutation_observer"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-265","content_html":"<p>I have recently got back into the flow for my side project <a href=\"https:\/\/comfortmonitor.live\">Comfort Monitor Live<\/a> and I have been building out the next feature which will allow users to customise the layout to their needs. To achieve that, I of course have had to rebuild my v1 solution from scratch as it no longer met my requirements.<\/p>\n<p>I always planned to use CSS grid to manage the layout, but I was initial expecting to do some calculations in the extension to calculate how many columns and rows to span and set positions, then I in some exploration of the MDN docs I came across the <code>grid-template-areas<\/code> and <code>grid-area<\/code> attributes and a light bulb went off in terms of how simple it could be.<\/p>\n<p>The <code>grid-template-areas<\/code> attribute works by allowing you to specify the layout via labels which are specified via the <code>grid-area<\/code> property. So by labeling the different elements that I would the user to have control over, I can then allow the user to, in effect, specify the <code>grid-template-areas<\/code> attribute themselves which means I am simply saving an array of strings. Below is a quick example of the CSS.<\/p>\n<pre class=\"language-css\"><code class=\"language-css\"><span class=\"token selector\"><span class=\"token id\">#fullscreen<\/span> <span class=\"token class\">.timer<\/span><\/span> <span class=\"token punctuation\">{<\/span>\n    <span class=\"token property\">grid-area<\/span><span class=\"token punctuation\">:<\/span> timer<span class=\"token punctuation\">;<\/span>\n<span class=\"token punctuation\">}<\/span>\n\n<span class=\"token selector\"><span class=\"token id\">#fullscreen<\/span> <span class=\"token class\">.clock<\/span><\/span> <span class=\"token punctuation\">{<\/span>\n    <span class=\"token property\">grid-area<\/span><span class=\"token punctuation\">:<\/span> clock<span class=\"token punctuation\">;<\/span>\n<span class=\"token punctuation\">}<\/span>\n\n<span class=\"token selector\"><span class=\"token id\">#fullscreen<\/span> <span class=\"token class\">.now<\/span><\/span> <span class=\"token punctuation\">{<\/span>\n    <span class=\"token property\">grid-area<\/span><span class=\"token punctuation\">:<\/span> now<span class=\"token punctuation\">;<\/span>\n<span class=\"token punctuation\">}<\/span>\n\n<span class=\"token selector\"><span class=\"token id\">#fullscreen<\/span> <span class=\"token class\">.next<\/span><\/span> <span class=\"token punctuation\">{<\/span>\n    <span class=\"token property\">grid-area<\/span><span class=\"token punctuation\">:<\/span> next<span class=\"token punctuation\">;<\/span>\n<span class=\"token punctuation\">}<\/span>\n\n<span class=\"token selector\"><span class=\"token id\">#cml-layout<\/span> <span class=\"token id\">#fullscreen<\/span><\/span> <span class=\"token punctuation\">{<\/span>\n    <span class=\"token property\">display<\/span><span class=\"token punctuation\">:<\/span> grid<span class=\"token punctuation\">;<\/span>\n    <span class=\"token property\">grid-template-columns<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token function\">repeat<\/span><span class=\"token punctuation\">(<\/span><span class=\"token number\">4<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token function\">minmax<\/span><span class=\"token punctuation\">(<\/span><span class=\"token number\">0<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token number\">1<\/span><span class=\"token unit\">fr<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n    <span class=\"token property\">grid-template-rows<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token function\">repeat<\/span><span class=\"token punctuation\">(<\/span><span class=\"token number\">4<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token function\">minmax<\/span><span class=\"token punctuation\">(<\/span><span class=\"token number\">0<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token number\">1<\/span><span class=\"token unit\">fr<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n    <span class=\"token property\">height<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token number\">100<\/span><span class=\"token unit\">vh<\/span><span class=\"token punctuation\">;<\/span>\n\n    <span class=\"token comment\">\/*\n      Below is an example of how the attribute works,\n      in the project I set this via Javascript.\n    *\/<\/span>\n    <span class=\"token property\">grid-template-areas<\/span><span class=\"token punctuation\">:<\/span>\n        <span class=\"token string\">&quot;timer timer timer timer&quot;<\/span>\n        <span class=\"token string\">&quot;timer timer timer timer&quot;<\/span>\n        <span class=\"token string\">&quot;clock clock now now&quot;<\/span>\n        <span class=\"token string\">&quot;clock clock next next&quot;<\/span><span class=\"token punctuation\">;<\/span>\n<span class=\"token punctuation\">}<\/span>\n<\/code><\/pre>\n<p>The only other thing to note is that when a label isn&#x27;t present I then need to hide the missing divs using <code>display: none<\/code> so they do not get place in the implicit grid the CSS would create.<\/p>\n<p>The other bit of Javascript which powers this feature is a MutationObserver which I&#x27;m going to cover in the next post.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-265","title":"Using CSS Grid to power flexible dynamic layouts","summary":"A small discovery on my part unlocked the next feature for Comfort Monitor Live","date_modified":"2025-01-23T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#css","#layout","#side_project"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-264","content_html":"<p>A quick up top, I am still here, but as promised I am not posting every day! Now, onto the article...<\/p>\n<p>A toot from the other day reminded me of an idea I had a while back before I was writing so much, I had shared within the Developer Tea Discord thinking-out-loud channel and thought it would be better served as a post here. Below is my original message:<\/p>\n<blockquote>\n<p>I was having a discussion today about carbon offsetting and it me remembering an <a href=\"https:\/\/dannyvankooten.com\/website-carbon-emissions\/\">article<\/a> about website carbon emissions. This got me thinking that it would be awesome if we have carbon reporting baked into the web (something like a HTTP response header) which could then allow individuals (through browsers or initially browser extensions) to use this data to either limit the amount of time on the web based on the amount of carbon a website used or for an individual to pay for the carbon they are using (hooking into a service like <a href=\"https:\/\/docs.ecologi.com\/\">ecologi<\/a>)<\/p>\n<\/blockquote>\n<blockquote>\n<p>I feel like doing this accurately would be near impossible, but I am tempted to try and give it a go for myself.<\/p>\n<\/blockquote>\n<p>As mentioned in the original post, this would be difficult, but would be pretty cool to see happen (or something similar). The idea hasn&#x27;t progressed since then, but at least today I have made it more public to the world!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-264","title":"Idea: Tracking our Carbon usage as web developers","summary":"Could our screen time be limited by the amount of CO2 used?","date_modified":"2025-01-16T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#idea","#web","#carbon"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-263","content_html":"<p>One thing I have noticed when making my first few contributions to Django is the difference in how I read comments from others depending on whether I have either met them of heard them speak before (eg on a podcast).<\/p>\n<p>Essentially the difference is I can hear their voice in my head as I am reading, their personality comes across much more, it feels more human. In comparison, others I haven&#x27;t met I am more likely to fall prey to the negativity bias, interpeting a comment much more critically in comparison. I primarily put this down to not hearing their tone of voice in the words I am reading.<\/p>\n<p>This led me to a potentially simple idea to enable a more human connection within the community. I wondered if community members could record a simple video or audio clip of themselves in which we would introduce ourselves and perhaps answer a few questions. This feels far more accessible than having to wait and have the money to meet in person (ie at a conference) or waiting to hear someone on a podcast. The video\/audio could be as simple as a loom video, although a self-hosted option would likely be more sustainable for the community.<\/p>\n<p>Could this work? The bigger question behind this idea is to build relationships within the community. For example I would be encouraged to watch\/listen someone&#x27;s clip before reading their review comments so I can hear their voice when I am reviewing their comments.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-263","title":"Idea: Building relationships in the community while doing the work","summary":"How do we combat the default negativity bias?","date_modified":"2025-01-10T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#open_source","#contribution","#idea","#django"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-262","content_html":"<p>As mentioned yesterday I completed my goal for 2024 to write at least 100 words every weekday for a year. It resulted in a book&#x27;s worth of content, some was good and the content came easily, some day&#x27;s I struggled to get the words down before the end of the day. The highlight was seeing my work get shared, be it in discord or further online and especially in the Django Newsletter!<\/p>\n<p>All that said I don&#x27;t particularly want to stop, but I don&#x27;t want myself to be forced to write something everyday, especially if it&#x27;s been busy and I don&#x27;t have anything to say. Some of my more interesting articles definitely took longer than just dumping words on the page. They required reading and some research to check my work.<\/p>\n<p>This goal has been fun and it made&#x27;s it&#x27;s mark on me, but change is needed for this to be sustainable while I explore other interests.<\/p>\n<p>So to the future:<\/p>\n<ul>\n<li>I&#x27;m experimenting with a podcast and I would like to get around to video as well.<\/li>\n<li>Writing has been excellent so I will continue, but perhaps 1-3 articles\/week instead of 1\/day. I also still have ideas to get out there!<\/li>\n<li>I want to also consolidate some of last year&#x27;s writing into longer pieces, this will take time and require some editing so they flow correctly.<\/li>\n<\/ul>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-262","title":"What is next...","summary":"I have completed the year, now what?","date_modified":"2025-01-09T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#future","#meta"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-261","content_html":"<p>On <a href=\"\/100-words\/day-243\">Day 243<\/a> I mentioned I was interested in getting a total word count, well today after a conversation with llama3.1 via my text editor it managed to spit out a script that seems fairly reasonably correct.<\/p>\n<p>I asked the AI to strip out links and code which seems to have happened well for the most part as well as ignoring the front matter for each of the mdx files. This it struggled with a bit more, but we eventually got there.<\/p>\n<p>So over 261 posts (including today) we have a total of 56608 words written this year, which is an average of word count of 217 words. Day 161 has my highest word count of 482 words and Day 26 being the shortest with 97 words (3 short!) after striping out the code. The only other rough statistic of interest is that most of the posts were in the 100 word to 200 word range, then 200-300 word range, with only a few getting longer than that.<\/p>\n<p>This post rounds off a year of writing every week day which was my goal for 2024. I&#x27;m not going to stop writing, but in terms of what is next, well that is for tomorrow&#x27;s post!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-261","title":"Word counting part 2","summary":"Now that I am at my year let us get a count","date_modified":"2025-01-08T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#word_count","#meta"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-260","content_html":"<p>This is a rift on a <a href=\"https:\/\/swizec.com\/blog\/finding-modules-in-a-big-ball-of-mud-with-chatgpt\/\">article<\/a> I read last year (I think?) from Swizec Teller. Yesterday&#x27;s post about management commands reminded me of this idea.<\/p>\n<p>Essenitally could our editor\/IDE or other tool automatically sort out where our code lives? In the context of Django I would be interested in this at the <code>apps<\/code> layer, but it could work at any layer really. The gist of what Swizec get&#x27;s at in the article (and some before it) is that when we first create some software, it&#x27;s typically a big ball of mud with everything depends on everything. In Django world this means either a load of different apps that depend on each other both in the python code and via relationships in the database or everything is in a single <code>models.py<\/code> and <code>views.py<\/code>.<\/p>\n<p>This works to a point, but it&#x27;s very hard to reason about the architecture of the system without reading every line of code. To borrow from keynote Tobias gave at DjangoCon Europe 2023, it forces you to start at the smallest chunk size to understand what is going on.<\/p>\n<p>Now imagine an interface where you can have a view of your code which hides directories and files then based on the imports you bring into the current code you are writing. Then the tool automatically would suggest the most appropriate file location in the codebase. Swizec suggests AI could help with this, which is possibly true, but I wonder if for certain base project structures a tool could be deterministic rather than rely on AI? I also wonder what the UX (or DX) would be like within the code editor for this to work, possibly something similar to an AI chat prompt that allows a developer to start working without deciding <em>where<\/em> to work first would be my initial go to.<\/p>\n<p>Would this be a tool that you would like to use? There is also considering the case as Swizec does of an existing tangled codebase needing organisation, that could be a Django Management command to some extent.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-260","title":"Auto sorting code imports","summary":"Could we just write code without worrying about where it lives...","date_modified":"2025-01-07T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#refactoring","#imports","#idea"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-259","content_html":"<p>Before you call me names, first a disclaimer, I think this hot take only applies to management commands that live in project (ie not a reusable app) and to projects that have decided to have multiple apps.<\/p>\n<p>Ok with that out the way, I do think that most management commands (that are native to projects), fall into one of the following categories.<\/p>\n<ol>\n<li>One off commands to correct\/migrate some data manually<\/li>\n<li>Commands that run regularly via cron<\/li>\n<li>Commands to setup some data or reset some data.<\/li>\n<\/ol>\n<p>Again there are other types of commands, but they are typically in a package and not in your end project. So given those categories most of them will likely depend on multiple apps if not all of them or are commands that should be removed once they have been used.<\/p>\n<p>Therefore, with all the above stated, I can make the bold claim that all management commands should live in the project and the project is installed as an app for this purpose. This ties in nicely as well if you start a project like this anyway!<\/p>\n<p>More seriously though this reminds me of a Swizec Teller article which I will expand on tomorrow.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-259","title":"Management Commands should only live at the project level","summary":"a bit of hot take...","date_modified":"2025-01-06T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#hot_take","#idea","#management_commands"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-258","content_html":"<p>Yesterday I mentioned that a potential role of a Research Fellow would be to develop &quot;Reference Architecture&quot; projects. But what do I mean by this?<\/p>\n<p>To me there often seems to be a gap between the theoretical side of (enterprise?) architecture that is held in tomes such as &quot;Designing Data-Intensive Applications&quot; (I own this but have only ever glanced through the contents), whitepapers from big tech or conference videos and the rather practical side of writing code that needs to be shipped asap for the startup to survive. It&#x27;s true that the majority of my experience has been in smaller companies and that larger corporates may have the time to explore these practices, but that&#x27;s thing, the code would still be locked away unlikely to see the light of day as it will contain proprietary intellectual property rights.<\/p>\n<p>This is where these projects come into play, they would form a bridge from the simple solution to exploring more complex architectures. They would all want to solve the same business problem to be some what comparable, even if the solutions for some architectures become a little naive. Just yesterday <a href=\"https:\/\/indiehackers.social\/deck\/@_chrismay@fosstodon.org\/113759771339135964\">Chris May<\/a> mentioned his exploration of Event Sourcing via the <a href=\"https:\/\/pypi.org\/project\/eventsourcing\/\">eventsourcing<\/a> package. Another example is the multiple times people ask about microservices (for better or worse), or using feature flags across a project.<\/p>\n<p>As I write this it&#x27;s perhaps that there is just too much content and choice out there to consume and understand, that is why companies grow and we don&#x27;t build everything ourselves single handedly! Either way I think these reference projects would be a valuable addition to corpus of knowledge that Django could provide, they could even be content that is monetized rather than open for all to use, thus contributing back to the Fellow&#x27;s program directly.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-258","title":"Building Reference Architecture Projects","summary":"Perhaps a missing gap between theory and practice","date_modified":"2025-01-03T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#idea","#architecture"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-257","content_html":"<p>This was first mentioned in one of Jeff&#x27;s office hours (the EU edition to be precise!) where someone brought up the idea in chat of a Documentation\/Technical Writing Fellow and a Security Fellow. Personally this struck me as a reasonably good idea and since then it has noodled in my brain over the holiday&#x27;s and below are some further thoughts.<\/p>\n<p>First a disclaimer, most of this will rely on increased funding to the DSF so they first hire an ED which ought to pave the way for further funding, the fellowship program being one recipient of the increased funding.<\/p>\n<p>When I think the word &#x27;fellow&#x27; it&#x27;s often it&#x27;s academic context that comes to mind (maybe because I live in Cambridge?) anyway, it made me think about the adjectives that go before the word, most notably there are &quot;Teaching Fellow&#x27;s&quot; and &quot;Research Fellow&#x27;s&quot;, alongside the above mentioned Documentation Fellow.<\/p>\n<p>Each of these would seem to have a place in the Django community, for example, a Teaching Fellow is someone paid by the DSF to produce teaching material for Django, be it written, audio or video. A Research Fellow could be charged with researching new features that are then proposed back to the community or researching and developing reference projects. The possibilities here are obviously endless and are perhaps more projects rather than roles to begin with so that the Fellows hired can get a feel for the existing responsibilties before we add new ones.<\/p>\n<p>One question that comes to mind though it how to frame the existing essential work that the Fellow&#x27;s do? It&#x27;s a mix of things that is likely worth defining (if it hasn&#x27;t been done already) as separate roles that could exist for separate individuals in the future? Personally I would want to ensure that these questions are answered before we add more specialised fellows to the mix.<\/p>\n<p>What do you think? Does this idea have legs or should it stay on the cutting room floor until some future date?<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-257","title":"Specialist Fellow Roles for Django","summary":"One potential idea for the fellowship program","date_modified":"2025-01-02T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#fellows","#contribution"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-256","content_html":"<p>Firstly, Happy New Year! I hope you enjoyed whatever you did to celebrate, I however was in bed with the flu (and I&#x27;m still dosed up right now...). For the last 4 years, my wife and I have intentionally set goals each year, just before the new year rolls around (illness has delayed this year!). We intentionally go through the different areas of our lives and decide what we want to get done in the next year, we then have a monthly check-in to review them and adjust course if neccessary.<\/p>\n<p>The life areas we cover are Family, Spirituality, Career, Health &amp; Fitness, Relationship, House, Goals for each child. Some goals overlap multiple areas which helps keep it managable over all. In each category we set about 2-3 goals either together or separately.<\/p>\n<p>Finally as a setup for the monthly review we will take each goal and break it down into what the initial steps are to complete the goal or what systems need to be put into place to ensure we meet the goal. As an example from last year, to ensure I wrote these posts every weekday, I added an event to my calendar and a recurring to do item in Todoist, or for some work on the house the first thing might be time to schedule a discussion in an evening to simply make time for a focused discussion. The key here is that no step is too small, often when tasks are too large then it&#x27;s easier for us to procrastinate on them (this is why the classic new year&#x27;s resolutions tend to fail for most people.)<\/p>\n<p>As a mini-preview of this year&#x27;s goals for my career are as follows (these might change as we have yet to discuss them together):<\/p>\n<ul>\n<li>Build a PoC for a new startup<\/li>\n<li>Do &#x27;more&#x27; in the Django community. I need to pin this down more, but it will likely include:\n<ul>\n<li>Working Group<\/li>\n<li>Producing content<\/li>\n<li>Building and releasing some packages<\/li>\n<\/ul>\n<\/li>\n<li>Maintain existing client relationships and revenue levels<\/li>\n<li>Build the next features for Comfort Monitor Live<\/li>\n<\/ul>\n<p>A final note is that it&#x27;s always worth considering a negative goal. Typically goals get framed as doing more, achieving more, where as a negative goal is looking at I ought to stop doing? This allows focus on what really matters.<\/p>\n<p>Let&#x27;s see how this goes for 2025!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-256","title":"Looking to the year ahead","summary":"What goal setting looks like for me","date_modified":"2025-01-01T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#goal_setting"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-255","content_html":"<p>Client work was at full steam during Q4, which meant juggling a few too many plates! That said one of my clients got acquired which meant I could drop a client to allow me more time for this client in future (we will likely be hiring next year so drop me a message!). In October I managed to get along to a DSF Office Hours and suggested a new working group which was warmly recieved, I opened the <a href=\"https:\/\/github.com\/django\/dsf-working-groups\/pull\/23\">PR<\/a> and now I just need a couple more members!<\/p>\n<p>November hurtled by as Christmas preparations were well underway as well as tricky Stripe migration which consumed a huge amount of time to ensure we had everything just right! and well it was worth it as we pulled it off with only a few minor issues. The main thing in December, other than Christmas celebrations (which there were a few!), was nominating myself for my first election which was for the Django Steering Council. It was an exciting prospect, but ultimately as with most things in life, those that marketed themselves better were picked. I&#x27;m excited for those that were elected and what they do over the next release cycle.<\/p>\n<p>Before I get to some reflections on the last year here are some notable highlights for me:<\/p>\n<ul>\n<li>Wrote 255 blog posts this year, some of these posts being included in the Django Newsletter!<\/li>\n<li>Got my first 2 paying customers for my side project, <a href=\"https:\/\/comfortmonitor.live\">Comfort Monitor Live<\/a>.<\/li>\n<li>Formally proposed a DSF working group.<\/li>\n<li>Nominated myself for the Django Steering Council (didn&#x27;t get it, but I have lot on so it&#x27;s probably for the best!).<\/li>\n<li>Picked to be a co-founder of a startup.<\/li>\n<li>Been freelance for 5 years and had my most successful year yet!<\/li>\n<li>Organised 11 Django Social events in Cambridge.<\/li>\n<li>Experimented with a podcast idea called <a href=\"\/in-progress\">in progress<\/a><\/li>\n<li>Mostly importantly enjoyed being a family of four!<\/li>\n<\/ul>\n<p>As I ponder 2024, the things that really stand out are being part of a communities are key to being healthy mentally, these communities are best (for me) when they straddle both online &amp; in-person. Next is to be planning for the future, this allowed us to handle my wife being on maternity leave and for the few quiet months to happen without major interruptions to our lives. Finally it&#x27;s ok to take a step back from something when life changes (ie a newborn!), I did about 6 runs this year, which is a lot less than the 3-4\/week I was doing last year. However, I continued bouldering and that has gone from fortnightly to weekly.<\/p>\n<p>I&#x27;m excited for 2025 and achieving just as much as I did this year!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-255","title":"A year in review - Q4","summary":"Ending the year on a high","date_modified":"2024-12-31T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#review"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-254","content_html":"<p>July brought in a new client and the start of the Djangonaut Space program. The first was welcome news for the runway that was quickly dimishing and the second was the push I needed to get stuck into contributing code to Django which resulted in me closing a couple of tickets on Django and also pushing forward on the idea of a working group.<\/p>\n<p>Also in the background I was still having conversation&#x27;s with Davin about the co-founder role which included meeting up in person a couple of times, then in early August I got the email telling me I was the lucky chosen person to be the co-founder. This resulted in a 48-hour (to the hour) trip to Dublin in early September to kick off discussions about building the proof of concept.<\/p>\n<p>July was a lot of birthday&#x27;s and camps, August was a time for the family holiday with a 2-week break in the Cotswolds which was nice but a teething baby and lots of driving meant it wasn&#x27;t as relaxing as other holidays we have had in the past. I also kept up the 100 word blogs during this holiday so not to break the habit. Finally in September we had our boiler replaced which was disruptive but worth it to be gas-free and on a cheaper tariff.<\/p>\n<p>Tomorrow is Q4 and some end of year reflections<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-254","title":"A year in review - Q3","summary":"Summer picks up the pace!","date_modified":"2024-12-30T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#review","#djangonaut_space"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-253","content_html":"<p>I enjoyed the early part of April as I had some rare time to myself and had a whole day reading, I do wish I had more day&#x27;s to do this, but life rarely allows for it. Client work slowed resulting me eating into my reserves which is never good for my mental health! So client searching began in earnest during this quarter, as I wondered where how to keep the business afloat and the market not looking great.<\/p>\n<p>During this quarter new relationships were formed, firstly with Davin who would eventually pick me to become a cofounder on a startup venture, and Wes where we have been chatting about an outreach mission experiment to the Django community, both of these at this point in the year were in there very early stages and I had no idea what the result would be at the time, but I trusted God to lead the way.<\/p>\n<p>In May saw meetups in London with DITFK and Being Freelance and Dad time with my eldest taking him to a Brick Festival (lots of Lego!), camping with friends and regular dog-sitting for a friend. June brought more family time with birthdays and applying for Djangonaut Space which simply applying for inched me further into the Django community. This kept me busy while waiting on potential prospects to get back to me (more failure here!).<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-253","title":"A year in review - Q2","summary":"A slow spring and early summer","date_modified":"2024-12-27T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#review"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-252","content_html":"<p>The new year started early for us as our second son arrived one week early on Boxing Day! (Today he is one!) Therefore January was settling into life as a family of four with client work taking a backseat and having the chance to do some side project work. I also started writing this series on the 9th Jan, luckily I had a backlog of topics to cover that had been sitting in Todoist for a while!<\/p>\n<p>Feburary resulted in me being fully back at to work servicing existing clients. I also joined the celebrations for the Being Freelance community enjoying their 1st year in Circle with some Brownie bake along and our home life being a bit hectic as we were preparing for our attic to be boarded to allow for better storage and space for our eldest to play Lego away from the newborn! Django Social also returned to Cambridge with co-working and our most popular to date from what I remember.<\/p>\n<p>March sped by with much of the same, the attic actually got boarded and it was the lead up to Easter. The habit of 100 words was also well estabished and I had setup publishing these as blog posts and automatically posting them to Mastodon and Linkedin. In this quarter I applyed to the Django Fellow role but failed, I mention this as it&#x27;s easy to gloss over failure&#x27;s, but it is only through failing that you truly succeed. This can clearly been seen when young children in your life, they deal with failure every single day, whether as baby learning to move or slightly older children at school learning the basics of reading and maths.<\/p>\n<p>Tomorrow onto Q2!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-252","title":"A year in review - Q1","summary":"New beginnings from January to March","date_modified":"2024-12-26T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#review"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-251","content_html":"<p>To all my readers over the last year, I just wanted to wish you a Merry Christmas! Thank you to all those who have engaged with my content either through likes, providing feedback or comments!<\/p>\n<p>My goal for this year was to write 100 words every week day which I have achieved starting slightly late on the 9th Jan. Therefore I&#x27;m not done until I make up for that shortfall at which point I may become slightly less frequent in my writing, only posting when I have something definite to write about. That said the habit has been built so it will hopefully still be frequent.<\/p>\n<p>I&#x27;m going to round off this year with a week long year in review as is the trend and perhaps talking about my goals for the next year.<\/p>\n<p>PS Don&#x27;t worry this post was written in advance of the 25th!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-251","title":"Merry Christmas!","summary":"a day of rest and for time with family","date_modified":"2024-12-25T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#holiday","#christmas","#celebration"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-250","content_html":"<p>Unless I&#x27;m missing some settings configuration (I am incredibly lazy in this regard), I would like to build a Django extension for the Zed editor. This times well as Josh Thomas has just released his <a href=\"https:\/\/indiehackers.social\/deck\/@josh@joshthomas.dev\/113705585645952494\">language server for Django<\/a> which Zed extensions supports. Generally Zed works really well for my liking, not that I have explored all the features available, however when saving Django templates the default formatting completely messes with the Django tags which has resulted me commiting bad code on a couple of occasions. As a result I typically have to switch to Plain Text to prevent Zed from messing with my templates.<\/p>\n<p>When will have I time with all my other commitments and ideas? I have no idea, but if there are any other Zed editor users that want this let me know and perhaps we can get something working together!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-250","title":"Django extension for Zed Editor","summary":"Saving template files are broken...","date_modified":"2024-12-24T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#zed","#idea","#extensions"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-249","content_html":"<p>Last week, when adding filtering for items based on permissions I wanted to use the <code>split_part<\/code> function from Postgres within Django. I had tried this before and failed, but this time I knew this is what I required so I could do my filtering in the minimal number of queries. I was about to jump into the docs and what would have been fairly lengthly coding and debugging session, at which point I thought to give an AI a go (Claude 3.5 Sonnet to be specific) and a couple of iterations later I had working database function class in my code as I continued onward with my higher line of thought (the feature I was building).<\/p>\n<p>You can see the end code artifact it produced <a href=\"https:\/\/claude.site\/artifacts\/53670660-189f-498e-ac58-2730e4dba2bc\">here<\/a>. This was the second iteration as the first failed to capture that delimiter should be wrapped in a <code>Value<\/code> class from Django.<\/p>\n<p>Why did this work so well? First and foremost, I (the human) knew what success of the end result would look like and I could easily verify this in my codebase. Secondly it reduced what would have been at least an hour of working out the code required and not to mention the error messages that would have likely been confusing to debug, into an aside of 5 minutes which allowed my thought process to be focused still on the feature I was building. Finally I can now take this code going forward as a template for other database functions I might require, the AI has provided me with a pattern to follow.<\/p>\n<p>The real key here is the first point I make. We as human&#x27;s need to know what success looks like and therefore be able to correct an AI when it is wrong.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-249","title":"Using AI effectively","summary":"Reducing speed bumps when building features","date_modified":"2024-12-23T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#ai","#database_functions"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-248","content_html":"<p>Continuing from yesterday, the point of this architecture choice is simplify the codebase and align the logic across multiple models, forms and views. However if it doesn&#x27;t work for the features we need then I&#x27;m not against adopting more usual patterns and packages to get the job done (ie django-taggit for tagging\/filtering).<\/p>\n<p>Before we get into how the filtering works, there is a slight correction from yesterday. It turns out Django Guardian is using a Generic Foreign Key under the hood to link everything together. What&#x27;s nice is the shortcut functions they provide and obviously the integration with the permissions framework.<\/p>\n<p>Back on filtering, it is slightly harder to wrap my head around which models we need to perform the filter on. However having the consistent language of the permissions and content type apps means some shortcut functions are now easy to use and hide most of the complexity. The next hurdle at some point is going to be searching these objects, hopefully by then I will have a polymorphic base model implemented to ease that solution!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-248","title":"Filtering lists using perimssions","date_modified":"2024-12-20T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#permissions","#guardian","#object-level"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-247","content_html":"<p>I&#x27;m currently in the middle of a architecture migration for one of my clients where are unifying how the sharing of different resources (essentially different models) are visible to different users. We have also built some UI to support this so some users have the ability to do the sharing themselves. For the models that are not yet migrated it&#x27;s either a ForeignKey or ManyToManyField named differently on each model and operating slighltly differently in each view.<\/p>\n<p>The basics of the new architecture is that we programitically create Django authorization Groups that are linked to differing levels of access, be it a single object, a group of users, or everyone. We then use django-guardian to assign the <code>view_*<\/code> permission to the group that matches the object being permissioned (this is linked via the pk of the object being present in the name of the group)<\/p>\n<p>Finally we have used this logic to allow users to group arbitary objects in to collections of items. This isn&#x27;t done explicity through relationships or Generic ForeignKeys, but it&#x27;s a semi-layered approach to permissions as the collection has an auth group which gets view permission to the objects added to the collection.<\/p>\n<p>All this results in quite generic and reusable code for both the sharing an object with a user and also for views where we want to display a list of objects that a user has access to. The current challenge is adding the ability for a user to filter on these list views, I was pondering this today and tomorrow I will share the results of my findings.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-247","title":"Working with Django Guardian","summary":"Powering a Django project through permissions","date_modified":"2024-12-19T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#permissions","#guardian","#object-level"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-246","content_html":"<p>This idea has been brewing for a while and finally came to fruition yesterday while discussing some <a href=\"https:\/\/github.com\/orgs\/django-commons\/discussions\/136#discussioncomment-11598050\">meet and greet ideas<\/a> in the Django Commons org.<\/p>\n<p>The broad idea is to start with a checklist of requirements that can be verified (ideally in an automated manner) for any package and would describe the overall health of a package. This checklist would then be split into different levels (think along the lines of Bronze, Silver, Gold etc). Then a package can then be awarded said badges, which could unlock other potential benefits such as eligibity to move into Django-Commons, inclusion in Django news for calling for contributors, placement within the official Django documentation or even financial benefit if the DSF had spare budget.<\/p>\n<p>This system means we don&#x27;t have necessarily choose any particular package as &#x27;blessed&#x27; but it is simply based on the health of the package itself. Of course that is where the difficulty lies! Coming up with the list of requirements and doing the assesment. It will also need collaboration across multiple parties, django-commons, djangopackages and the proposed package working group come to mind as immediate interested parties. Additionally this endorsement could apply to any package in the Django ecosystem so badges could be applied to anyone to demonstrate their level to developers and prospective contributors.<\/p>\n<p>What do you think? Does this have potential? Let me know!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-246","title":"Endorsing Django Packages","summary":"Yet another idea for highlighting packages","date_modified":"2024-12-18T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#packages","#third-party"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-245","content_html":"<p>Management commands to me are a core interaction with any Django project. They are the command line interface to acting on a Django project in a manner, this ranges from a development server, moving files around, dealing with migrations or helping with debugging using a python shell, and this is just the commands that come with Django core.<\/p>\n<p>Management commands really come into their own when you add them to your project. I have used them to setup a blank project with seed data, migrated data in a controlled manner (ie not during a deployment) and that is just in the last couple of weeks. Finally when combined with *nix cron they are a simple way to get code running at regular intervals, which is perfect for polling an external API and updating or database, sending out emails (or other notifications) or extracting data from your application to another location.<\/p>\n<p>Writing a command is covered in the <a href=\"https:\/\/docs.djangoproject.com\/en\/5.1\/howto\/custom-management-commands\/\">docs in depth<\/a>, however it&#x27;s simple to get started, the below code gets put in the folder <code>management\/commands\/&lt;my_command_name&gt;.py<\/code><\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token keyword\">from<\/span> django<span class=\"token punctuation\">.<\/span>core<span class=\"token punctuation\">.<\/span>management<span class=\"token punctuation\">.<\/span>base <span class=\"token keyword\">import<\/span> BaseCommand\n\n<span class=\"token keyword\">class<\/span> <span class=\"token class-name\">Command<\/span><span class=\"token punctuation\">(<\/span>BaseCommand<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    <span class=\"token builtin\">help<\/span> <span class=\"token operator\">=<\/span> <span class=\"token string\">&quot;Some help text&quot;<\/span>\n\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">handle<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">,<\/span> <span class=\"token operator\">*<\/span>args<span class=\"token punctuation\">,<\/span> <span class=\"token operator\">**<\/span>options<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        <span class=\"token comment\"># code goes here!<\/span>\n<\/code><\/pre>\n<p>That&#x27;s it! The command is then available at <code>manage.py &lt;command_name&gt;<\/code>.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-245","title":"Django Management Commands in a 100 words","summary":"One off commands, developer tooling and simple scheduled tasks","date_modified":"2024-12-17T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#management_commands"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-244","content_html":"<p>I recently bought myself an <a href=\"https:\/\/uk.espres.so\/\">espresso 13 inch monitor<\/a> to expand my portable setup wth my laptop. However I also recently discovered that my Fairphone 5 has a desktop mode. This means I can plugin in the screen and a desktop environment will pop up. This allows me to connect a bluetooth mouse and keyboard to my phone for a lightweight desktop to use out and about!<\/p>\n<p>I then wondered if were possible to develop code on my phone. It turns out I can with the aid of <a href=\"https:\/\/termux.dev\/en\/\">Termux<\/a> I was able to run a blank django project locally and the use of <a href=\"https:\/\/acode.app\/\">Acode<\/a> I think I have a local environment that is pretty close to my laptop setup. The larger question is one of performance, for example how quick could it run tests? and what effect will that have on my phone battery?<\/p>\n<p>Only time will tell on this front, but I am excited to perhaps be able to travel lighter!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-244","title":"Developing Django on my mobile","summary":"An experiment in travelling lighter","date_modified":"2024-12-16T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#development","#remote_working","#portable_office","#equipment"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-243","content_html":"<p>As the end of this year of blog approaches it&#x27;s end (I have about 20 more days to go), I am curious to know how many words I have written in total as most of these blog posts have gone over 100 words, probably averaging in the 200 - 300 word range.<\/p>\n<p>However I have been wondering how best to count how much I have written, do I include code and links in the count, what about any metadata? This is where we get into the mechanics of doing the counting. As I see it there are two options.<\/p>\n<p>First would be to parse the <code>mdx<\/code> files which are the source files for these posts, stripping out any metadata and code then summing them to get a total. In fact I have tried this using an AI to generate a script which needs some tweaking<\/p>\n<p>The other approach would be to scrape the website or RSS feed and pull out the relevant content to then get a total count.<\/p>\n<p>Honestly I&#x27;m not sure which would the best approach, I&#x27;m open to suggestions! (Let me know with a reply or a comment)<\/p>\n<p>...except doing the count manually, I don&#x27;t have time for that!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-243","title":"How much have I written this year...","summary":"A lot that is for sure, but how do I get an accurate number","date_modified":"2024-12-13T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#meta","#word_count","#writing"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-242","content_html":"<p>Another result of my template mini-project was stumbling across <a href=\"https:\/\/code.djangoproject.com\/ticket\/26007\">ticket #26007<\/a> as I incorrectly configured my CreateView and UpdateView&#x27;s by specifying a queryset instead of a model as the class attribute. This led to a confusing error message that I hadn&#x27;t <code>template_name<\/code> on the class. This was confusing as I wanted to use the predefined default template name of <code>&lt;app_name&gt;\/&lt;model_name&gt;&lt;suffix&gt;.html<\/code>. Browsing Trac led me to the above ticket which eventually allowed me to realise my mistake.<\/p>\n<p>However the ticket had me intruiged, so earlier this week I asked a question to gauge whether the docstring of <code>get_template_names<\/code> was incorrect or whether code should be improved. This led to me submitting a PR yesterday for updating the docstring and improving the second error message to be clearer. It&#x27;s still in progress as a PR <a href=\"https:\/\/github.com\/django\/django\/pull\/18923\">here<\/a>.<\/p>\n<p>Hopefully this will get merged this side of Christmas, but we will see.<\/p>\n<p>PS I also learnt in this progress that a Generic CBV can grab a template reference stored in the database or stored on the model referenced in the class. This feels like it could be very powerful, but potentially dangerous feature!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-242","title":"Ticket #26007","summary":"Picking up a new ticket","date_modified":"2024-12-12T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#contribution"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-241","content_html":"<p>Over the weekend when polishing my new template repo for Django, in adding auth I expected there to be a simple <code>RegisterView<\/code> to use similar to <code>LoginView<\/code> (side note, it makes me realise that I haven&#x27;t spun up a proper Django project from scratch in a long time!). It should be noted that this proposal is not new, Will Vincent has a recent <a href=\"https:\/\/forum.djangoproject.com\/t\/update-startproject-with-default-login-signup-logout-options\/35175\">forum post<\/a> about this exact thing.<\/p>\n<p>Perhaps this post can serve as pushing that forum post forward a notch? To keep in line with Will&#x27;s proposal we need to add a URL, View &amp; Template, so without further ado let&#x27;s get show some code.<\/p>\n<p>URL pattern:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\">  path<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;accounts\/register&quot;<\/span><span class=\"token punctuation\">,<\/span> RegisterView<span class=\"token punctuation\">.<\/span>as_view<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span> name<span class=\"token operator\">=<\/span><span class=\"token string\">&#x27;register&#x27;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n<\/code><\/pre>\n<p>View:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token keyword\">class<\/span> <span class=\"token class-name\">RegisterView<\/span><span class=\"token punctuation\">(<\/span>CreateView<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    model <span class=\"token operator\">=<\/span> get_user_model<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n    form_class <span class=\"token operator\">=<\/span> UserCreationForm\n    template_name <span class=\"token operator\">=<\/span> <span class=\"token string\">&#x27;registration\/register.html&#x27;<\/span>\n    success_url <span class=\"token operator\">=<\/span> settings<span class=\"token punctuation\">.<\/span>LOGIN_REDIRECT_URL\n<\/code><\/pre>\n<p>Template:<\/p>\n<pre class=\"language-html\"><code class=\"language-html\">{% extends &quot;base.html&quot; %}\n\n{% block content %}\n{% if form.errors %}\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>p<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ form.errors }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>p<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n{% endif %}\n\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>form<\/span> <span class=\"token attr-name\">method<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>post<span class=\"token punctuation\">&quot;<\/span><\/span> <span class=\"token attr-name\">action<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>{% url <span class=\"token punctuation\">&#x27;<\/span>register<span class=\"token punctuation\">&#x27;<\/span> %}<span class=\"token punctuation\">&quot;<\/span><\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n{% csrf_token %}\n{{ form }}\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>input<\/span> <span class=\"token attr-name\">type<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>submit<span class=\"token punctuation\">&quot;<\/span><\/span> <span class=\"token attr-name\">value<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>login<span class=\"token punctuation\">&quot;<\/span><\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>input<\/span> <span class=\"token attr-name\">type<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>hidden<span class=\"token punctuation\">&quot;<\/span><\/span> <span class=\"token attr-name\">name<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>next<span class=\"token punctuation\">&quot;<\/span><\/span> <span class=\"token attr-name\">value<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>{{ next }}<span class=\"token punctuation\">&quot;<\/span><\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>form<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n\n{# Assumes you set up the login view in your URLconf #}\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>p<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>a<\/span> <span class=\"token attr-name\">href<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>{% url <span class=\"token punctuation\">&#x27;<\/span>login<span class=\"token punctuation\">&#x27;<\/span> %}<span class=\"token punctuation\">&quot;<\/span><\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Already have an account? Sign in here<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>a<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>p<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n{% endblock content %}\n<\/code><\/pre>\n<p>This is likely missing the configuration that you get from the other authentication views (I think), but it&#x27;s a notch forward!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-241","title":"Pushing a Proposal forward: RegisterView into contrib.auth","summary":"This is not new, just my two cents from recent expectations","date_modified":"2024-12-11T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#authentication","#contributions","#proposals"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-240","content_html":"<p>One issue that fairly often trips up beginners with Django is understanding the relationship and timing of template rendering and Javascript execution. This confusion is often compounded by the nature of a typical local development setup, where the server (runserver) and the client (a browser) are on the same machine.<\/p>\n<p>Typically the question goes along the lines of trying to manipulate some data from the view with Javascript directly or somehting similar. So let&#x27;s clarify a few things here.<\/p>\n<p>First there are two locations to consider with a Django project, the server and the client. The server is where Django templates are rendered and sent to the client (a browser) where the Javascript is executed. This last sentence should also reveal that the timing of when different bits of code are executed. The template is rendered first, then the Javascript, therefore it is impossible for any Javascript to have an effect on the template render process.<\/p>\n<p>What is possible is to make an other request to the server once the client has loaded the initial page. This is known an &quot;Asynchronous JavaScript And XML&quot; (AJAX) request, with a many JS libraries making this kind of thing much easier such as HTMX, axios or the plain fetch API. Alternatively there are some Django packages that help with this such as Django-unicorn that provide an abstraction around this behaviour.<\/p>\n<p>Hopefully, this will mean those beginners won&#x27;t be trying to do time travel to make their applications work!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-240","title":"Understanding space and time travel with Django","summary":"Knowing where and where templates execute in relation to the browser","date_modified":"2024-12-10T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#templates","#server-client"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-239","content_html":"<p>After releasing the initial version of my template project on Friday to have a link to share in the post, I went through the project over the weekend to check everything worked and polished some of the rough edges. This meant adding authentication, the admin, a few more views that I had mentioned earlier and fixing bugs so it would all work as intended.<\/p>\n<p>While doing this I picked up on an existing Django <a href=\"https:\/\/code.djangoproject.com\/ticket\/26007\">ticket<\/a> that I want to fix. Also I realised Django is sorely missing a builtin <code>RegisterView<\/code>, there is a <a href=\"https:\/\/forum.djangoproject.com\/t\/update-startproject-with-default-login-signup-logout-options\/35175\">forum post<\/a> to get one added though, so that&#x27;s something.<\/p>\n<p>Finally, doing this template project made me wonder a few further ideas. First, how much should be added to a <code>startproject<\/code> template? Do we add deployment configuration? How about dependencies? At what point does it get too close to cookiecutter templates? Second I remembered Tom has an open DEP about having a new command <code>django new<\/code> so I want to read all that and make an input. Third I wonder what other templates would be appreciated and whether developers would be willing to pay for them? A small fee to use a template that illustrates a good starting point for a project or demonstrates how to solve a particular problem (eg multiple user profiles in Django). Let me know if this would of interest or just a bad idea.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-239","title":"Building a web application a 100 words at a time - Bonus","summary":"Updates to the template project after some testing","date_modified":"2024-12-09T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#tutorial","#startproject","#template"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-238","content_html":"<p>This part of the project is new ground for me, I have never created a template project before so my up was checking the docs which is very sparse and just has some reference details on using a template but nothing about creating a project. Next I remembered that Adam Johnson had created his own <a href=\"https:\/\/github.com\/adamchainz\/django-startproject-templates\">template repo<\/a>, this provided me with a starting point, but my project is going to be more detailed in terms of included code. Finally I checked if a ticket existed to expand the documentation on this, it turns out there is someone working on the ticket with a <a href=\"https:\/\/code.djangoproject.com\/ticket\/35485\">patch in progress<\/a>!<\/p>\n<p>Having done some sufficient research, all that was left was to combine the last eight days of code into a project and make the necessary adjustments to get it to work as a template project. I have made my first commit to this <a href=\"https:\/\/github.com\/softwarecrafts\/django-todo-project-template\">template repository<\/a>. I expect this to require more work such as:<\/p>\n<ul>\n<li>Check it works as a template project!<\/li>\n<li>Adding migrations<\/li>\n<li>Finishing the management command and refactoring it to not use factory boy<\/li>\n<li>Static file configuration.<\/li>\n<li>Any other bugs, this project was created with what I term BDD (Blog driven development), I have yet to run this code as it has all been written in the context of these posts.<\/li>\n<\/ul>\n<p>If you want to use this template project feel free to raise an issue or a PR please.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-238","title":"Building a web application a 100 words at a time - 9","summary":"Wrapping this project up as a template to be used!","date_modified":"2024-12-06T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#tutorial","#startproject","#template"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-237","content_html":"<p>Our last addition to this small project is some automated tests. There are two main camps when it comes to Django testing, unittest or pytest.<\/p>\n<p>For simplicity, we&#x27;re going to stick to the built in Django TestCase which uses unittest. When testing a Django project the first thing to writes tests for is the views as this gets the most coverage of the codebase. Below are a couple of example test cases for a list view and create view.<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token keyword\">from<\/span> django<span class=\"token punctuation\">.<\/span>test <span class=\"token keyword\">import<\/span> TestCase\n\n<span class=\"token keyword\">class<\/span> <span class=\"token class-name\">ProjectListViewTestCase<\/span><span class=\"token punctuation\">(<\/span>TestCase<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">test_list<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        Project<span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span>create<span class=\"token punctuation\">(<\/span>name<span class=\"token operator\">=<\/span><span class=\"token string\">&#x27;Project 1&#x27;<\/span><span class=\"token punctuation\">,<\/span> description<span class=\"token operator\">=<\/span><span class=\"token string\">&quot;test description&quot;<\/span><span class=\"token punctuation\">)<\/span>\n        Project<span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span>create<span class=\"token punctuation\">(<\/span>name<span class=\"token operator\">=<\/span><span class=\"token string\">&#x27;Project 2&#x27;<\/span><span class=\"token punctuation\">,<\/span> description<span class=\"token operator\">=<\/span><span class=\"token string\">&quot;test description&quot;<\/span><span class=\"token punctuation\">)<\/span>\n        Project<span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span>create<span class=\"token punctuation\">(<\/span>name<span class=\"token operator\">=<\/span><span class=\"token string\">&#x27;Project 3&#x27;<\/span><span class=\"token punctuation\">,<\/span> description<span class=\"token operator\">=<\/span><span class=\"token string\">&quot;test description&quot;<\/span><span class=\"token punctuation\">)<\/span>\n        response <span class=\"token operator\">=<\/span> self<span class=\"token punctuation\">.<\/span>client<span class=\"token punctuation\">.<\/span>get<span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;\/projects\/&#x27;<\/span><span class=\"token punctuation\">)<\/span>\n        self<span class=\"token punctuation\">.<\/span>assertContains<span class=\"token punctuation\">(<\/span>response<span class=\"token punctuation\">,<\/span> <span class=\"token string\">&#x27;&lt;dt&gt;&lt;a href=&quot;\/projects\/1&quot;&gt;Project 1&lt;\/a&gt;&lt;\/dt&gt;&#x27;<\/span><span class=\"token punctuation\">,<\/span> html<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">)<\/span>\n        self<span class=\"token punctuation\">.<\/span>assertContains<span class=\"token punctuation\">(<\/span>response<span class=\"token punctuation\">,<\/span> <span class=\"token string\">&#x27;&lt;dt&gt;&lt;a href=&quot;\/projects\/2&quot;&gt;Project 2&lt;\/a&gt;&lt;\/dt&gt;&#x27;<\/span><span class=\"token punctuation\">,<\/span> html<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">)<\/span>\n        self<span class=\"token punctuation\">.<\/span>assertContains<span class=\"token punctuation\">(<\/span>response<span class=\"token punctuation\">,<\/span> <span class=\"token string\">&#x27;&lt;dt&gt;&lt;a href=&quot;\/projects\/3&quot;&gt;Project 3&lt;\/a&gt;&lt;\/dt&gt;&#x27;<\/span><span class=\"token punctuation\">,<\/span> html<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">)<\/span>\n\n<span class=\"token keyword\">class<\/span> <span class=\"token class-name\">ProjectCreateViewTestCase<\/span><span class=\"token punctuation\">(<\/span>TestCase<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">test_valid_form<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        response <span class=\"token operator\">=<\/span> self<span class=\"token punctuation\">.<\/span>client<span class=\"token punctuation\">.<\/span>post<span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;\/projects\/create&#x27;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token punctuation\">{<\/span>\n            <span class=\"token string\">&quot;name&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">&#x27;My New Project&#x27;<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;description&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">&quot;This is a test project&quot;<\/span><span class=\"token punctuation\">,<\/span>\n        <span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span>\n        self<span class=\"token punctuation\">.<\/span>assertRedirects<span class=\"token punctuation\">(<\/span>response<span class=\"token punctuation\">,<\/span> <span class=\"token string\">&quot;\/projects\/1&quot;<\/span><span class=\"token punctuation\">)<\/span>\n        project <span class=\"token operator\">=<\/span> Project<span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span>get<span class=\"token punctuation\">(<\/span>pk<span class=\"token operator\">=<\/span><span class=\"token number\">1<\/span><span class=\"token punctuation\">)<\/span>\n        self<span class=\"token punctuation\">.<\/span>assertEqual<span class=\"token punctuation\">(<\/span>project<span class=\"token punctuation\">.<\/span>title<span class=\"token punctuation\">,<\/span> <span class=\"token string\">&#x27;My New Project&#x27;<\/span><span class=\"token punctuation\">)<\/span>\n        self<span class=\"token punctuation\">.<\/span>assertEqual<span class=\"token punctuation\">(<\/span>project<span class=\"token punctuation\">.<\/span>description<span class=\"token punctuation\">,<\/span>  <span class=\"token string\">&quot;This is a test project&quot;<\/span><span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>There are two key things to remember when testing, first is to keep in mind what precisely you are testing. It&#x27;s very easy to get off track and test the code you have written and not create the correct state needed for the test. The second is more of a process thought, be sure to cover every scenario; this means for every if statement there should be two tests. It&#x27;s easy to start with the happy path, but to be sure to create tests for errors as well.<\/p>\n<p>Tomorrow we will pull this all together into a template project.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-237","title":"Building a web application a 100 words at a time - 8","summary":"Adding tests to a project","date_modified":"2024-12-05T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#tutorial","#webapps","#testing"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-236","content_html":"<p>On <a href=\"\/100-words\/day-234\">Day 234<\/a>, I completely forgot how to show how form rendering works in terms of the templates. Since we didn&#x27;t customise the <code>template_name<\/code> attribute on the CreateView&#x27;s or UpdateView&#x27;s, then the defaults will be expected from Django. This means we need to create the following templates:<\/p>\n<ul>\n<li><code>app_name\/project_form.html<\/code><\/li>\n<li><code>app_name\/task_form.html<\/code><\/li>\n<\/ul>\n<p>Both of these templates are used by 2 views respectively and will be almost identical in terms of content. Here is <code>project_form.html<\/code><\/p>\n<pre class=\"language-html\"><code class=\"language-html\">{% extends &quot;base.html&quot; %}\n{% block page_title %}\n{% if object %}\nUpdate {{ object }}\n{% else %}\nNew Project\n{% endif %}\n{% endblock page_title %}\n\n{% block main_content %}\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>form<\/span> <span class=\"token attr-name\">method<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>post<span class=\"token punctuation\">&quot;<\/span><\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    {% csrf_token %}\n    {{ form }}\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>form<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n{% endblock main_content %}\n<\/code><\/pre>\n<p>and likewise <code>task_form.html<\/code>:<\/p>\n<pre class=\"language-html\"><code class=\"language-html\">{% extends &quot;base.html&quot; %}\n{% block page_title %}\n{% if object %}\nUpdate {{ object }}\n{% else %}\nNew Task\n{% endif %}\n{% endblock page_title %}\n\n{% block main_content %}\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>form<\/span> <span class=\"token attr-name\">method<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>post<span class=\"token punctuation\">&quot;<\/span><\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    {% csrf_token %}\n    {{ form }}\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>form<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n{% endblock main_content %}\n<\/code><\/pre>\n<p>The only difference here is the page title for creation, so we could make these views and templates even more generic, but alas this is not the goal of this application.<\/p>\n<p>Tomorrow we add some simple tests, then it will be wrapping this up into a template project to be used with <code>startproject<\/code> at the end of the week.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-236","title":"Building a web application a 100 words at a time - 7","summary":"Rendering forms in templates","date_modified":"2024-12-04T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#tutorial","#webapps","#forms","#html"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-235","content_html":"<p>One of the final aspects to this template project is to add some basic styling to the project, for this we need to configure our static settings and add a css file and refactor our templates.<\/p>\n<p>First up is to configure our staticfiles settings. This ought to be as simple as just setting <code>STATIC_ROOT<\/code> and <code>STATIC_URL<\/code> to appropriate values. I covered this on <a href=\"\/100-words\/day-55\">Day 55<\/a>.<\/p>\n<p>Next I&#x27;m going to use <a href=\"https:\/\/picocss.com\/docs\">PicoCSS<\/a> to provide some basic styling without us having to write lots of CSS. We&#x27;re going to manually download the <a href=\"https:\/\/github.com\/picocss\/pico\/archive\/refs\/heads\/main.zip\">files<\/a> into a static folder and then add this line to our templates:<\/p>\n<pre class=\"language-html\"><code class=\"language-html\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>link<\/span> <span class=\"token attr-name\">rel<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>stylesheet<span class=\"token punctuation\">&quot;<\/span><\/span> <span class=\"token attr-name\">href<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>{% static <span class=\"token punctuation\">&#x27;<\/span>css\/pico.min.css<span class=\"token punctuation\">&#x27;<\/span> %}<span class=\"token punctuation\">&quot;<\/span><\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n<\/code><\/pre>\n<p>However adding that line repeatedly to our three template files (with more to come as any project expands) is going to be tedious and error prone, therefore let&#x27;s create a new template called <code>base.html<\/code> which looks like this:<\/p>\n<pre class=\"language-html\"><code class=\"language-html\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>html<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>head<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>title<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{% block head_title %}My To Do List{% endblock head_title %}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>title<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>link<\/span> <span class=\"token attr-name\">rel<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>stylesheet<span class=\"token punctuation\">&quot;<\/span><\/span> <span class=\"token attr-name\">href<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>{% static <span class=\"token punctuation\">&#x27;<\/span>css\/pico.min.css<span class=\"token punctuation\">&#x27;<\/span> %}<span class=\"token punctuation\">&quot;<\/span><\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>head<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>body<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>h1<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{% block page_title %}{% endblock page_title %}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>h1<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>main<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n        {% block main_content %}\n        {% endblock main_content %}\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>main<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>body<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>html<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n<\/code><\/pre>\n<p>Next we refactor the existing templates we have created from <a href=\"\/100-words\/day-333\">Day 333<\/a> and Day 334 (not that I explained this yesterday, I completely forgot!). So our basic templates become as follows:<\/p>\n<p><code>projects\/list.html<\/code>:<\/p>\n<pre class=\"language-html\"><code class=\"language-html\">{% extends &quot;base.html&quot; %}\n{% block page_title %}Project List{% endblock page_title %}\n\n{% block main_content %}\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dl<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    {% for project in projects %}\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>a<\/span> <span class=\"token attr-name\">href<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>{% url <span class=\"token punctuation\">&#x27;<\/span>project_detail<span class=\"token punctuation\">&#x27;<\/span> project.pk %}<span class=\"token punctuation\">&quot;<\/span><\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ project.name }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>a<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ project.description }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    {% endfor %}\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dl<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n{% endblock main_content %}\n<\/code><\/pre>\n<p><code>projects\/detail.html<\/code>:<\/p>\n<pre class=\"language-html\"><code class=\"language-html\">{% extends &quot;base.html&quot; %}\n{% block page_title %}Project Detail: {{ project.name }}{% endblock page_title %}\n\n{% block main_content %}\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dl<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Description:<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ project.description }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Created at:<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ project.created_at }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Last Updated:<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ project.updated_at }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Created by:<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ project.created_by }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dl<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>table<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>thead<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n                <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>th<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Task Name<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>th<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n                <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>th<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Reported By<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>th<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n                <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>th<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Assigned To<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>th<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n                <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>th<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Due Date<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>th<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n                <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>th<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Created<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>th<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>thead<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>tbody<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            {% for task in tasks %}\n                <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>td<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>a<\/span> <span class=\"token attr-name\">href<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>{% url <span class=\"token punctuation\">&#x27;<\/span>task_detail<span class=\"token punctuation\">&#x27;<\/span> task.pk %}<span class=\"token punctuation\">&quot;<\/span><\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ task.name }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>a<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>td<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n                <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>td<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ task.reported_by }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>td<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n                <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>td<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ task.assigned_to }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>td<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n                <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>td<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ task.due_date }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>td<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n                <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>td<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ task.created_by }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>td<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            {% endfor %}\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>tbody<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>table<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n{% endblock main_content %}\n<\/code><\/pre>\n<p><code>tasks\/detail.html<\/code>:<\/p>\n<pre class=\"language-html\"><code class=\"language-html\">{% extends &quot;base.html&quot; %}\n{% block page_title %}Task Detail: {{ task.name }}{% endblock page_title %}\n\n{% block main_content %}\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dl<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Description:<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ task.description }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Project:<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ task.project }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Created at:<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ task.created_at }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Last Updated:<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ task.updated_at }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Due Date:<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ task.due_date }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Reported by:<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ task.reported_by }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Assigned to:<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ task.assigned_to }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    {% if task.parent_task %}\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Parent Task:<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ task.parent_task }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    {% endif %}\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dl<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n{% endblock main_content %}\n<\/code><\/pre>\n<p>This now means all our templates extend the <code>base.html<\/code> file so they should all have some styling applied to them from pico.css.<\/p>\n<p>Tomorrow I&#x27;m going to back and cover the templates for the edit and update pages with what we haved covered here today.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-235","title":"Building a web application a 100 words at a time - 6","summary":"Styling with CSS and templates refactor","date_modified":"2024-12-03T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#tutorial","#webapps","#styling","#css"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-234","content_html":"<p>Now that we are displaying data on a web page, next we want to allow users to add data into our project. For this we require forms. Within a Django project, there are HTML forms and Django forms, they are very much related and overlap but are different.<\/p>\n<p>Django forms are written in Python and are primarily about validating data that has been submitted to the server, but they can also render a HTML form with in a template, so generally useful. For this project there are three immediate use cases for a form:<\/p>\n<ol>\n<li>Create\/Edit a Project<\/li>\n<li>Create\/Edit a Task<\/li>\n<li>Performing more specialised action (eg completing a task or adding a due date)<\/li>\n<\/ol>\n<p>For this base project, we&#x27;re just going to cover the initial two cases. First is to create the forms themselves:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token keyword\">from<\/span> django <span class=\"token keyword\">import<\/span> forms\n<span class=\"token keyword\">from<\/span> <span class=\"token punctuation\">.<\/span>models <span class=\"token keyword\">import<\/span> Project<span class=\"token punctuation\">,<\/span> Task\n\n<span class=\"token keyword\">class<\/span> <span class=\"token class-name\">ProjectForm<\/span><span class=\"token punctuation\">(<\/span>forms<span class=\"token punctuation\">.<\/span>ModelForm<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    <span class=\"token keyword\">class<\/span> <span class=\"token class-name\">Meta<\/span><span class=\"token punctuation\">:<\/span>\n        model <span class=\"token operator\">=<\/span> Project\n        fields <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">(<\/span>\n            <span class=\"token string\">&quot;name&quot;<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;description&quot;<\/span><span class=\"token punctuation\">,<\/span>\n        <span class=\"token punctuation\">)<\/span>\n\n<span class=\"token keyword\">class<\/span> <span class=\"token class-name\">TaskForm<\/span><span class=\"token punctuation\">(<\/span>forms<span class=\"token punctuation\">.<\/span>ModelForm<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    <span class=\"token keyword\">class<\/span> <span class=\"token class-name\">Meta<\/span><span class=\"token punctuation\">:<\/span>\n        model <span class=\"token operator\">=<\/span> Task\n        fields <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">(<\/span>\n            <span class=\"token string\">&quot;name&quot;<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;description&quot;<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;parent_task&quot;<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;assigned_to&quot;<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;project&quot;<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;due_date&quot;<\/span><span class=\"token punctuation\">,<\/span>\n        <span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>Here we use a ModelForm to simplify the amount of code we need write ourselves. Next we want to create a couple of views to expose this form, along with the corresponding URL patterns:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token keyword\">from<\/span> django<span class=\"token punctuation\">.<\/span>views<span class=\"token punctuation\">.<\/span>generic <span class=\"token keyword\">import<\/span> CreateView<span class=\"token punctuation\">,<\/span> UpdateView\n\n<span class=\"token keyword\">from<\/span> <span class=\"token punctuation\">.<\/span>models <span class=\"token keyword\">import<\/span> Project<span class=\"token punctuation\">,<\/span> Task\n<span class=\"token keyword\">from<\/span> <span class=\"token punctuation\">.<\/span>forms <span class=\"token keyword\">import<\/span> ProjectForm<span class=\"token punctuation\">,<\/span> TaskForm\n\n<span class=\"token keyword\">class<\/span> <span class=\"token class-name\">ProjectCreateView<\/span><span class=\"token punctuation\">(<\/span>CreateView<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    queryset <span class=\"token operator\">=<\/span> Project<span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span><span class=\"token builtin\">all<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n    form_class <span class=\"token operator\">=<\/span> ProjectForm\n    success_url <span class=\"token operator\">=<\/span> reverse_lazy<span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;project_list&#x27;<\/span><span class=\"token punctuation\">)<\/span>\n\n<span class=\"token keyword\">class<\/span> <span class=\"token class-name\">TaskCreateView<\/span><span class=\"token punctuation\">(<\/span>CreateView<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    queryset <span class=\"token operator\">=<\/span> Task<span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span><span class=\"token builtin\">all<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n    form_class <span class=\"token operator\">=<\/span> TaskForm\n\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">get_success_url<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        <span class=\"token keyword\">return<\/span> reverse_lazy<span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;task_detail&#x27;<\/span><span class=\"token punctuation\">,<\/span> kwargs<span class=\"token operator\">=<\/span><span class=\"token punctuation\">{<\/span><span class=\"token string\">&#x27;task_id&#x27;<\/span><span class=\"token punctuation\">:<\/span> self<span class=\"token punctuation\">.<\/span>instance<span class=\"token punctuation\">.<\/span>pk<span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span>\n\n<span class=\"token keyword\">class<\/span> <span class=\"token class-name\">ProjectUpdateView<\/span><span class=\"token punctuation\">(<\/span>UpdateView<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    queryset <span class=\"token operator\">=<\/span> Project<span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span><span class=\"token builtin\">all<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n    form_class <span class=\"token operator\">=<\/span> ProjectForm\n\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">get_success_url<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        <span class=\"token keyword\">return<\/span> reverse_lazy<span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;project_detail&#x27;<\/span><span class=\"token punctuation\">,<\/span> kwargs<span class=\"token operator\">=<\/span><span class=\"token punctuation\">{<\/span><span class=\"token string\">&#x27;project_id&#x27;<\/span><span class=\"token punctuation\">:<\/span> self<span class=\"token punctuation\">.<\/span>instance<span class=\"token punctuation\">.<\/span>pk<span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span>\n\n<span class=\"token keyword\">class<\/span> <span class=\"token class-name\">TaskUpdateView<\/span><span class=\"token punctuation\">(<\/span>UpdateView<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    queryset <span class=\"token operator\">=<\/span> Task<span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span><span class=\"token builtin\">all<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n    form_class <span class=\"token operator\">=<\/span> TaskForm\n\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">get_success_url<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        <span class=\"token keyword\">return<\/span> reverse_lazy<span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;task_detail&#x27;<\/span><span class=\"token punctuation\">,<\/span> kwargs<span class=\"token operator\">=<\/span><span class=\"token punctuation\">{<\/span><span class=\"token string\">&#x27;task_id&#x27;<\/span><span class=\"token punctuation\">:<\/span> self<span class=\"token punctuation\">.<\/span>instance<span class=\"token punctuation\">.<\/span>pk<span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>Finally our URL patterns:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span>\n    path<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;projects\/create&quot;<\/span><span class=\"token punctuation\">,<\/span> ProjectCreateView<span class=\"token punctuation\">.<\/span>as_view<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span> name<span class=\"token operator\">=<\/span><span class=\"token string\">&quot;project_create&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n    path<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;tasks\/create&quot;<\/span><span class=\"token punctuation\">,<\/span> TaskCreateView<span class=\"token punctuation\">.<\/span>as_view<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span> name<span class=\"token operator\">=<\/span><span class=\"token string\">&quot;task_create&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n    path<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;projects\/&lt;pk:project_id&gt;\/edit &quot;<\/span><span class=\"token punctuation\">,<\/span> ProjectUpdateView<span class=\"token punctuation\">.<\/span>as_view<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span> name<span class=\"token operator\">=<\/span><span class=\"token string\">&quot;project_update&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n    path<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;tasks\/&lt;pk:task_id&gt;\/edit&quot;<\/span><span class=\"token punctuation\">,<\/span> TaskUpdateView<span class=\"token punctuation\">.<\/span>as_view<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span> name<span class=\"token operator\">=<\/span><span class=\"token string\">&quot;task_update&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n<\/code><\/pre>\n<p>Now users are able to add\/edit projects &amp; tasks. We have 2 final steps, first is adding some CSS to style the frontend, and then package this up to use as a template project, I should also add some tests!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-234","title":"Building a web application a 100 words at a time - 5","summary":"Inputting data via forms","date_modified":"2024-12-02T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#tutorial","#webapps","#forms"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-233","content_html":"<p>Yesterday we dumped some data from our views into the web browser, but it likely wasn&#x27;t all that helpful to an end user. Today we are going to fix that issue with templates. Templates are HTML files that are marked up with specific syntax to render data passed from a view.<\/p>\n<p>First up we need to change the return of our views to use the <code>render<\/code> function:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token keyword\">from<\/span> django<span class=\"token punctuation\">.<\/span>shortcuts <span class=\"token keyword\">import<\/span> render\n<span class=\"token keyword\">from<\/span> <span class=\"token punctuation\">.<\/span>models <span class=\"token keyword\">import<\/span> Project<span class=\"token punctuation\">,<\/span> Task\n\n<span class=\"token keyword\">def<\/span> <span class=\"token function\">project_list<\/span><span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    <span class=\"token keyword\">return<\/span> render<span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">,<\/span> <span class=\"token string\">&#x27;projects\/list.html&#x27;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token punctuation\">{<\/span>\n        <span class=\"token string\">&quot;projects&quot;<\/span><span class=\"token punctuation\">:<\/span> Project<span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span><span class=\"token builtin\">filter<\/span><span class=\"token punctuation\">(<\/span>created_by<span class=\"token operator\">=<\/span>request<span class=\"token punctuation\">.<\/span>user<span class=\"token punctuation\">)<\/span>\n    <span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span>\n\n<span class=\"token keyword\">def<\/span> <span class=\"token function\">project_detail<\/span><span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">,<\/span> project_id<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    project <span class=\"token operator\">=<\/span> get_object_or_404<span class=\"token punctuation\">(<\/span>Project<span class=\"token punctuation\">,<\/span> pk<span class=\"token operator\">=<\/span>project_id<span class=\"token punctuation\">)<\/span>\n    <span class=\"token keyword\">if<\/span> project<span class=\"token punctuation\">.<\/span>created_by <span class=\"token operator\">!=<\/span> request<span class=\"token punctuation\">.<\/span>user<span class=\"token punctuation\">:<\/span>\n        <span class=\"token keyword\">raise<\/span> Http404<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n    <span class=\"token keyword\">return<\/span> render<span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">,<\/span> <span class=\"token string\">&#x27;projects\/detail.html&#x27;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token punctuation\">{<\/span>\n        <span class=\"token string\">&quot;project&quot;<\/span><span class=\"token punctuation\">:<\/span> project<span class=\"token punctuation\">,<\/span>\n        <span class=\"token string\">&quot;tasks&quot;<\/span><span class=\"token punctuation\">:<\/span> project<span class=\"token punctuation\">.<\/span>tasks<span class=\"token punctuation\">.<\/span><span class=\"token builtin\">all<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n    <span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span>\n\n\n<span class=\"token keyword\">def<\/span> <span class=\"token function\">task_detail<\/span><span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">,<\/span> task_id<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    task <span class=\"token operator\">=<\/span> get_object_or_404<span class=\"token punctuation\">(<\/span>Task<span class=\"token punctuation\">,<\/span> pk<span class=\"token operator\">=<\/span>task_id<span class=\"token punctuation\">)<\/span>\n    <span class=\"token keyword\">return<\/span> render<span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">,<\/span> <span class=\"token string\">&#x27;tasks\/detail.html&#x27;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token punctuation\">{<\/span>\n        <span class=\"token string\">&quot;task&quot;<\/span><span class=\"token punctuation\">:<\/span> task\n    <span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>Next we need to create the three files specified in our views, specifically <code>projects\/list.html<\/code>, <code>projects\/detail.html<\/code> and <code>tasks\/detail.html<\/code>. These directories and files live within a <code>templates<\/code> directory within a Django app. Below are each template:<\/p>\n<p><code>projects\/list.html<\/code>:<\/p>\n<pre class=\"language-html\"><code class=\"language-html\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>html<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>head<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>title<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>My To Do List<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>title<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>head<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>body<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>h1<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Project List<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>h1<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dl<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            {% for project in projects %}\n                <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>a<\/span> <span class=\"token attr-name\">href<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>{% url <span class=\"token punctuation\">&#x27;<\/span>project_detail<span class=\"token punctuation\">&#x27;<\/span> project.pk %}<span class=\"token punctuation\">&quot;<\/span><\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ project.name }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>a<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n                <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ project.description }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            {% endfor %}\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dl<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>body<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>html<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n<\/code><\/pre>\n<p><code>projects\/detail.html<\/code>:<\/p>\n<pre class=\"language-html\"><code class=\"language-html\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>html<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>head<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>title<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>My To Do List<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>title<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>head<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>body<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>h1<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Project Detail: {{ project.name }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>h1<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dl<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Description:<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ project.description }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Created at:<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ project.created_at }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Last Updated:<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ project.updated_at }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Created by:<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ project.created_by }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dl<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>table<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>thead<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n                <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>th<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Task Name<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>th<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n                <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>th<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Reported By<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>th<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n                <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>th<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Assigned To<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>th<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n                <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>th<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Due Date<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>th<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n                <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>th<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Created<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>th<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>thead<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>tbody<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            {% for task in tasks %}\n                <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>td<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>a<\/span> <span class=\"token attr-name\">href<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>{% url <span class=\"token punctuation\">&#x27;<\/span>task_detail<span class=\"token punctuation\">&#x27;<\/span> task.pk %}<span class=\"token punctuation\">&quot;<\/span><\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ task.name }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>a<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>td<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n                <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>td<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ task.reported_by }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>td<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n                <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>td<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ task.assigned_to }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>td<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n                <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>td<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ task.due_date }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>td<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n                <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>td<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ task.created_by }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>td<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            {% endfor %}\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>tbody<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>table<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>body<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>html<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n<\/code><\/pre>\n<p><code>tasks\/detail.html<\/code>:<\/p>\n<pre class=\"language-html\"><code class=\"language-html\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>html<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>head<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>title<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>My To Do List<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>title<\/span><span class=\"token punctuation\">&gt;<\/span><\/span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>head<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>body<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>h1<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Task Detail: {{ task.name }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>h1<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dl<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Description:<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ task.description }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Project:<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ task.project }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Created at:<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ task.created_at }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Last Updated:<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ task.updated_at }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Due Date:<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ task.due_date }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Reported by:<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ task.reported_by }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Assigned to:<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ task.assigned_to }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            {% if task.parent_task %}\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>Parent Task:<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dt<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{ task.parent_task }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dd<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            {% endif %}\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>dl<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>body<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>html<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n<\/code><\/pre>\n<p>Phew! That was a lot of code! As you can a fair amount of that is repeated boilerplate which we will revisit next week. This will also look very plain, again we will revist that next week as well. However we should now be able to visit all 3 page types via links and it be readable to an end user.<\/p>\n<p>We could do with adding a few extras like a navigation bar to get back to the project list easily or some pages to see tasks assigned to the current user.<\/p>\n<p>In our next post we will add a form to create new projects and tasks.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-233","title":"Building a web application a 100 words at a time - 4","summary":"Rendering data nicely","date_modified":"2024-11-29T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#tutorial","#webapps","#templates"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-232","content_html":"<p>Now that we have our models defined and some data in our database we can start visualise the data in our web browser. This requires three parts of Django, URL patterns, views and templates, today we will just look at URL patterns and views.<\/p>\n<p>For our to-do project we first need to decide what pages would be useful, to start there would be the following:<\/p>\n<ul>\n<li>Dashboard index page<\/li>\n<li>Project List<\/li>\n<li>Single project with task list<\/li>\n<li>Single Task<\/li>\n<li>Create Task<\/li>\n<li>Create Project<\/li>\n<li>Delete Project<\/li>\n<li>Delete Task<\/li>\n<li>Edit Task<\/li>\n<li>Edit Project<\/li>\n<\/ul>\n<p>Today we will focus on the simpler pages, with the other pages to follow once we have covered forms. First up is our views, to begin with I am going to use function based views so that we have a mix in the project.<\/p>\n<pre class=\"language-py\"><code class=\"language-py\">\n<span class=\"token keyword\">from<\/span> django<span class=\"token punctuation\">.<\/span>http <span class=\"token keyword\">import<\/span> HttpResponse\n<span class=\"token keyword\">from<\/span> <span class=\"token punctuation\">.<\/span>models <span class=\"token keyword\">import<\/span> Project<span class=\"token punctuation\">,<\/span> Task\n\n<span class=\"token keyword\">def<\/span> <span class=\"token function\">project_list<\/span><span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    <span class=\"token keyword\">return<\/span> HttpResponse<span class=\"token punctuation\">(<\/span>Project<span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span><span class=\"token builtin\">filter<\/span><span class=\"token punctuation\">(<\/span>created_by<span class=\"token operator\">=<\/span>request<span class=\"token punctuation\">.<\/span>user<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">)<\/span>\n\n<span class=\"token keyword\">def<\/span> <span class=\"token function\">project_detail<\/span><span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">,<\/span> project_id<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    project <span class=\"token operator\">=<\/span> get_object_or_404<span class=\"token punctuation\">(<\/span>Project<span class=\"token punctuation\">,<\/span> pk<span class=\"token operator\">=<\/span>project_id<span class=\"token punctuation\">)<\/span>\n    <span class=\"token keyword\">if<\/span> project<span class=\"token punctuation\">.<\/span>created_by <span class=\"token operator\">!=<\/span> request<span class=\"token punctuation\">.<\/span>user<span class=\"token punctuation\">:<\/span>\n        <span class=\"token keyword\">raise<\/span> Http404<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n    <span class=\"token keyword\">return<\/span> HttpResponse<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">{<\/span>\n        <span class=\"token string\">&quot;project&quot;<\/span><span class=\"token punctuation\">:<\/span> project<span class=\"token punctuation\">,<\/span>\n        <span class=\"token string\">&quot;tasks&quot;<\/span><span class=\"token punctuation\">:<\/span> project<span class=\"token punctuation\">.<\/span>tasks<span class=\"token punctuation\">.<\/span><span class=\"token builtin\">all<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n    <span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span>\n\n\n<span class=\"token keyword\">def<\/span> <span class=\"token function\">task_detail<\/span><span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">,<\/span> task_id<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    task <span class=\"token operator\">=<\/span> get_object_or_404<span class=\"token punctuation\">(<\/span>Task<span class=\"token punctuation\">,<\/span> pk<span class=\"token operator\">=<\/span>task_id<span class=\"token punctuation\">)<\/span>\n    <span class=\"token keyword\">return<\/span> HttpResponse<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">{<\/span>\n        <span class=\"token string\">&quot;task&quot;<\/span><span class=\"token punctuation\">:<\/span> task\n    <span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>Next add some URL patterns, more could be added to cover more specific use cases (eg tasks with in a project)<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token keyword\">from<\/span> django<span class=\"token punctuation\">.<\/span>urls <span class=\"token keyword\">import<\/span> path\n\n<span class=\"token keyword\">from<\/span> <span class=\"token punctuation\">.<\/span>views <span class=\"token keyword\">import<\/span> project_list<span class=\"token punctuation\">,<\/span> project_detail<span class=\"token punctuation\">,<\/span> task_detail\n\nurlpatterns <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">[<\/span>\n    path<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;projects\/&quot;<\/span><span class=\"token punctuation\">,<\/span> project_list<span class=\"token punctuation\">,<\/span> name<span class=\"token operator\">=<\/span><span class=\"token string\">&quot;project_list&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n    path<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;projects\/&lt;pk:project_id&gt;&quot;<\/span><span class=\"token punctuation\">,<\/span> project_detail<span class=\"token punctuation\">,<\/span> name<span class=\"token operator\">=<\/span><span class=\"token string\">&quot;project_detail&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n    path<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;tasks\/&lt;pk:task_id&gt;&quot;<\/span><span class=\"token punctuation\">,<\/span> task_detail<span class=\"token punctuation\">,<\/span> name<span class=\"token operator\">=<\/span><span class=\"token string\">&quot;task_detail&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n<span class=\"token punctuation\">]<\/span>\n<\/code><\/pre>\n<p>This should result in something appearing on a webpage, however it won&#x27;t be particularly useful to an end user to look at. This is where templates come into play which we will cover tomorrow.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-232","title":"Building a web application a 100 words at a time - 3","summary":"Adding URL patterns and views","date_modified":"2024-11-28T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#tutorial","#webapps"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-231","content_html":"<p>This idea is not an original from me, I first saw <a href=\"https:\/\/www.revsys.com\/tidbits\/devdata-improving-developer-velocity-and-experience\/\">this written<\/a> about by Revsys. The essential idea is to have a management command that adds initial required data for the application to work and any optional test data for you to work with to either test or develop a feature.<\/p>\n<p>Typically for this command I would use a library like <a href=\"https:\/\/factoryboy.readthedocs.io\/en\/stable\/\">factory boy<\/a> to create factories that can be used here and in the tests for the application. This gives use the flexibility to change parameters in code and easily generate fake data in comparison with the brittle nature and inflexible built in JSON fixture from Django.<\/p>\n<p>The beginnings of the command would look like this:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token keyword\">from<\/span> django<span class=\"token punctuation\">.<\/span>core<span class=\"token punctuation\">.<\/span>management<span class=\"token punctuation\">.<\/span>base <span class=\"token keyword\">import<\/span> BaseCommand\n\n<span class=\"token keyword\">class<\/span> <span class=\"token class-name\">Command<\/span><span class=\"token punctuation\">(<\/span>BaseCommand<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    <span class=\"token triple-quoted-string string\">&quot;&quot;&quot;\n    Uses Factories to setup objects in the database so the platform\n    can work correctly.\n\n    This will be used to reset staging or future sandbox environments\n    &quot;&quot;&quot;<\/span>\n\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">handle<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">,<\/span> <span class=\"token operator\">**<\/span>options<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        <span class=\"token keyword\">if<\/span> settings<span class=\"token punctuation\">.<\/span>ENVIRONMENT <span class=\"token operator\">==<\/span> <span class=\"token string\">&quot;production&quot;<\/span><span class=\"token punctuation\">:<\/span>\n            <span class=\"token keyword\">return<\/span>\n        self<span class=\"token punctuation\">.<\/span>main<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">main<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        self<span class=\"token punctuation\">.<\/span>setup_users<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n        self<span class=\"token punctuation\">.<\/span>setup_projects<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n        self<span class=\"token punctuation\">.<\/span>setup_tasks<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">setup_users<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        self<span class=\"token punctuation\">.<\/span>admin <span class=\"token operator\">=<\/span> UserFactory<span class=\"token punctuation\">(<\/span>is_superuser<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">,<\/span> is_staff<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">)<\/span>\n\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">setup_projects<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        self<span class=\"token punctuation\">.<\/span>project_1 <span class=\"token operator\">=<\/span> ProjectFactory<span class=\"token punctuation\">(<\/span>name<span class=\"token operator\">=<\/span><span class=\"token string\">&#x27;Christmas setup&#x27;<\/span><span class=\"token punctuation\">)<\/span>\n\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">setup_tasks<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        t1 <span class=\"token operator\">=<\/span> TaskFactory<span class=\"token punctuation\">(<\/span>name<span class=\"token operator\">=<\/span><span class=\"token string\">&#x27;Wrap presents&#x27;<\/span><span class=\"token punctuation\">,<\/span> project<span class=\"token operator\">=<\/span>self<span class=\"token punctuation\">.<\/span>project_1<span class=\"token punctuation\">)<\/span>\n        TaskFactory<span class=\"token punctuation\">(<\/span>name<span class=\"token operator\">=<\/span><span class=\"token string\">&#x27;Buy presents&#x27;<\/span><span class=\"token punctuation\">,<\/span> project<span class=\"token operator\">=<\/span>self<span class=\"token punctuation\">.<\/span>project_1<span class=\"token punctuation\">,<\/span> parent_task<span class=\"token operator\">=<\/span>t1<span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>Tis would want to be expanded to cover typical cases that you would want to check and interact with as well as filling in any required data that the application expects to exist (eg currencies for a budget tracker).<\/p>\n<p>Tomorrow we will start displaying this data with some views.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-231","title":"Building a web application a 100 words at a time - 2","summary":"Adding test data to the project","date_modified":"2024-11-27T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#tutorial","#webapps"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-230","content_html":"<p>Following on from yesterday, I&#x27;m going to walk through the building of the project I suggested yesterday, along with how I think about building out a web application.<\/p>\n<p>To me, there are two starting points to an application (or feature) once you have the idea locked in. First is the user experience (UX), second is the data model. Both should be equally important to the builder, the UX dictates the interaction and essentially the flow of data from the user to the database and back. The data model however is the main reasoning point for the builder. An incorrect data model will mean complex code over the long haul, which will lead to more bugs and maintenance burden, a correct data model will lead to simpler, elegant code which typically leads to higher productivity.<\/p>\n<p>The quote from Linus Torvalds sums up the data model side nicely: &quot;Bad programmers worry about the code. Good programmers worry about data structures and their relationships.&quot;. Also I like this quote from Swizec Teller - <a href=\"https:\/\/swizec.com\/blog\/why-sql-is-forever\/\">&quot;Data is forever&quot;<\/a><\/p>\n<p>All that said, the UX typically starts as a wireframe or mockup outside of code. Data models can start outside of code, but Django makes it just as easy to start within code. So without further ado, our initial models.py.<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token keyword\">class<\/span> <span class=\"token class-name\">Project<\/span><span class=\"token punctuation\">(<\/span>models<span class=\"token punctuation\">.<\/span>Model<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    name <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>CharField<span class=\"token punctuation\">(<\/span>max_length<span class=\"token operator\">=<\/span><span class=\"token number\">255<\/span><span class=\"token punctuation\">)<\/span>\n    description <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>TextField<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n    created_at <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>DateTimeField<span class=\"token punctuation\">(<\/span>auto_now_add<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">)<\/span>\n    updated_at <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>DateTimeField<span class=\"token punctuation\">(<\/span>auto_now<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">)<\/span>\n    created_by <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>ForeignKey<span class=\"token punctuation\">(<\/span>User<span class=\"token punctuation\">,<\/span> on_delete<span class=\"token operator\">=<\/span>models<span class=\"token punctuation\">.<\/span>CASCADE<span class=\"token punctuation\">,<\/span> related_name<span class=\"token operator\">=<\/span><span class=\"token string\">&#x27;projects&#x27;<\/span><span class=\"token punctuation\">)<\/span>\n\n<span class=\"token keyword\">class<\/span> <span class=\"token class-name\">Task<\/span><span class=\"token punctuation\">(<\/span>models<span class=\"token punctuation\">.<\/span>Model<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    parent_task <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>ForeignKey<span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;self&#x27;<\/span><span class=\"token punctuation\">,<\/span> null<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">,<\/span> blank<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">,<\/span> symmetrical<span class=\"token operator\">=<\/span><span class=\"token boolean\">False<\/span><span class=\"token punctuation\">,<\/span> related_name<span class=\"token operator\">=<\/span><span class=\"token string\">&#x27;sub_tasks&#x27;<\/span><span class=\"token punctuation\">)<\/span>\n    reported_by <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>ForeignKey<span class=\"token punctuation\">(<\/span>User<span class=\"token punctuation\">,<\/span> on_delete<span class=\"token operator\">=<\/span>models<span class=\"token punctuation\">.<\/span>CASCADE<span class=\"token punctuation\">,<\/span> related_name<span class=\"token operator\">=<\/span><span class=\"token string\">&#x27;reported_tasks&#x27;<\/span><span class=\"token punctuation\">)<\/span>\n    assigned_to <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>ForeignKey<span class=\"token punctuation\">(<\/span>User<span class=\"token punctuation\">,<\/span> on_delete<span class=\"token operator\">=<\/span>models<span class=\"token punctuation\">.<\/span>CASCADE<span class=\"token punctuation\">,<\/span> related_name<span class=\"token operator\">=<\/span><span class=\"token string\">&#x27;assigned_tasks&#x27;<\/span><span class=\"token punctuation\">)<\/span>\n    project <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>ForeignKey<span class=\"token punctuation\">(<\/span>Project<span class=\"token punctuation\">,<\/span> on_delete<span class=\"token operator\">=<\/span>models<span class=\"token punctuation\">.<\/span>CASCADE<span class=\"token punctuation\">,<\/span> related_name<span class=\"token operator\">=<\/span><span class=\"token string\">&#x27;tasks&#x27;<\/span><span class=\"token punctuation\">)<\/span>\n    name <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>CharField<span class=\"token punctuation\">(<\/span>max_length<span class=\"token operator\">=<\/span><span class=\"token number\">255<\/span><span class=\"token punctuation\">)<\/span>\n    description <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>TextField<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n    created_at <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>DateTimeField<span class=\"token punctuation\">(<\/span>auto_now_add<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">)<\/span>\n    updated_at <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>DateTimeField<span class=\"token punctuation\">(<\/span>auto_now<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">)<\/span>\n    due_date <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>DateTimeField<span class=\"token punctuation\">(<\/span>null<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">,<\/span> blank<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>There are other fields I could add, which would enable other features, but this is a solid starting point. Tomorrow we will add a management command to load predefined data easily into the application.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-230","title":"Building a web application a 100 words at a time - 1","summary":"This is going to a long series!","date_modified":"2024-11-26T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#tutorial","#webapps"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-229","content_html":"<p>A couple of weeks back I asked how people made time for contributing back and for experimenting with numerous Django packages available in the ecosystem.<\/p>\n<p>A partial idea, which was half validated by comment of Carlton&#x27;s (&quot;cut and paste models&quot;), is to have a starter project which has enough code and content to have something to experiment with, but not having too much in terms of custom packages to allow for experimentation of different packages.<\/p>\n<p>Off the top of my head it would need the following:<\/p>\n<ol>\n<li>Some models<\/li>\n<li>Some urls and views<\/li>\n<li>A couple of forms<\/li>\n<li>No JS in the frontend<\/li>\n<li>Vanilla CSS<\/li>\n<li>Management Command to load in data or a bundle sqlite file.<\/li>\n<\/ol>\n<p>Then perhaps bundling this up into a <code>startproject<\/code> template for reusability and distribution. The project would be a to-do list with Projects, Tasks and Subtasks as the main models.<\/p>\n<p>Would this be of interest to help others? If so, let me know! Otherwise I think I&#x27;m going to do a build along over the next few days\/weeks as I build out this little project to experiment with.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-229","title":"Building a template project","summary":"A standard starting point for experimentation","date_modified":"2024-11-25T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#contributions"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-228","content_html":"<p>More than a few times I have have wanted to do the following:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token keyword\">try<\/span><span class=\"token punctuation\">:<\/span>\n    obj<span class=\"token punctuation\">.<\/span>reverse_related_field<span class=\"token punctuation\">.<\/span>get<span class=\"token punctuation\">(<\/span><span class=\"token operator\">**<\/span>kwargs<span class=\"token punctuation\">)<\/span>\n<span class=\"token keyword\">except<\/span> obj<span class=\"token punctuation\">.<\/span>reverse_related_field<span class=\"token punctuation\">.<\/span>DoesNotExist<span class=\"token punctuation\">:<\/span>\n    <span class=\"token comment\"># deal with the exception<\/span>\n<\/code><\/pre>\n<p>Only to find that the exception <code>DoesNotExist<\/code> is not available like that so I am left to import <code>ObjectDoesNotExist<\/code> manually, so it becomes<\/p>\n<pre class=\"language-py\"><code class=\"language-py\">\n<span class=\"token keyword\">from<\/span> django<span class=\"token punctuation\">.<\/span>core<span class=\"token punctuation\">.<\/span>exceptions <span class=\"token keyword\">import<\/span> ObjectDoesNotExist\n<span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span>\n\n<span class=\"token keyword\">try<\/span><span class=\"token punctuation\">:<\/span>\n    obj<span class=\"token punctuation\">.<\/span>reverse_related_field<span class=\"token punctuation\">.<\/span>get<span class=\"token punctuation\">(<\/span><span class=\"token operator\">**<\/span>kwargs<span class=\"token punctuation\">)<\/span>\n<span class=\"token keyword\">except<\/span> ObjectDoesNotExist<span class=\"token punctuation\">:<\/span>\n    <span class=\"token comment\"># deal with the exception<\/span>\n<\/code><\/pre>\n<p>On the surface this seems like an easish win to me, but I will openly admit I could see the potential for it to be a rabbit hole of complexity that I am completely unaware of as I have yet to really dive into the code and before I did I thought writing about it would be best to gauge the reaction from the community. Also this may well have been proposed before and decided against (though I couldn&#x27;t see a forum post or ticket).<\/p>\n<p>So over to you, my reader you for this, against it or don&#x27;t care?<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-228","title":"Django proposal: Adding DoesNotExist and MultipleObjectsReturned exceptions to the Related Manager","summary":"Fixing a papercut I hit on occasion","date_modified":"2024-11-22T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#proposal","#contributing"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-227","content_html":"<p>It&#x27;s all news this week in the community, first we have the results of the Board Elections, congratulations to Tom, Paulo, Afi &amp; Jeff! I looking forward to seeing your proposals take shape along with the rest of the board.<\/p>\n<p>Also as the title mentions, Steering Council elections have offically started, which means I need to get my nominations statement in order. If you have been reading along over the last month, then you will have seen my <a href=\"\/100-words\/day-203\">two<\/a> <a href=\"\/100-words\/day-218\">wishlist<\/a> articles. These will essentially form the basis on my pitch after some editing to make the pitch more cohesive and maybe an extra idea or two.<\/p>\n<p>The final bit of news is that tickets for DjangoCon Europe went of <a href=\"https:\/\/pretix.evolutio.pt\/evolutio\/djceu2025\/\">sale<\/a> this week, grab a ticket while the price is low and see you in Dublin next year.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-227","title":"Steering Council Elections announced","summary":"...and other community news","date_modified":"2024-11-21T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#community","#steering-council"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-226","content_html":"<p>For one of my clients, I am in the middle of migrating from an existing Stripe account to a new Stripe account. We are doing this in a single big bang instead of a phased approach as the preparation time has been relatively minimal. We are doing the dry run on our staging server this week and so far so good in terms of the setup.<\/p>\n<p>However I hit a snag towards the end of today. I had installed and configured <a href=\"https:\/\/github.com\/fabiocaccamo\/django-maintenance-mode\/\">django-maintenance-mode<\/a> to take the site down and display a nice page while doing the migration. This all worked ok locally, however when testing this on staging I am getting the default nginx 503 response (I think) and the logs are not being helpful so far.<\/p>\n<p>I have left it for today and hopefully some sleep with yield some insight for tomorrow! Sometimes you just need to take a break.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-226","title":"Progress but nothing completed","summary":"Today has been a bit frustrating","date_modified":"2024-11-20T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#debugging","#migration"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-225","content_html":"<p>Today, we are back on the idea of partial views. This is a bad name as each of the views are complete whole views, a better name is the less catchy &quot;single responsiblity views&quot; because that&#x27;s what&#x27;s happening here, each view does one thing and one thing only.<\/p>\n<p>Last week I mentioned some issues in regard to templates and today I remembered that I thought Carlton&#x27;s <a href=\"https:\/\/github.com\/carltongibson\/django-template-partials\">django-template-partials<\/a> would fit somewhere into this idea and I think it&#x27;s the starting point of it, maybe, more thinking time is needed but initial ideas and questions that have popped into my head:<\/p>\n<ol>\n<li>In regard to partials, can they be nested, in that can partial named &#x27;one&#x27; be called within rendered within partial called &#x27;two&#x27;?<\/li>\n<li>A known limitation of partials is that they need to be defined within the same file. I can understand this limitation, but if partials could be defined in other files then could be a way forward.<\/li>\n<li>Could child block tags take multiple names to be rendered in different places? or render the found unrendered name before stopping?<\/li>\n<\/ol>\n<p>I am going to play with the nested partials first, to see if they work.<\/p>\n<p>One final thing I do want to mention is that there are a few Django libraries out there doing &#x27;reactive&#x27; components or building abstractions on top of views. They are excellent libaries and definitely have their place, however with this I am trying to be as lightweight as possible which means minimal additions to a normal Django application.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-225","title":"Partial Views - 3","summary":"What do templates look like for partial views","date_modified":"2024-11-19T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#templates","#views"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-224","content_html":"<p><a href=\"\/100-words\/day-222\">Last week<\/a> I proposed the idea of borrowing the idea of Error Boundaries from React. <a href=\"https:\/\/indiehackers.social\/deck\/@adamghill@indieweb.social\/113481464589156194\">Adam G Hill<\/a> liked the idea and is the author of dj-angles which is the perfect type of package to explore an idea like this.<\/p>\n<p>The next day he came back with an <a href=\"https:\/\/indiehackers.social\/deck\/@adamghill@indieweb.social\/113487335980220731\">initial implementation<\/a>, whch was then <a href=\"https:\/\/indiehackers.social\/deck\/@adamghill@indieweb.social\/113493035398085102\">improved<\/a> and eventually he has a prototype of an <a href=\"https:\/\/indiehackers.social\/deck\/@adamghill@indieweb.social\/113495238271204471\">error boundary tag<\/a> in <a href=\"https:\/\/dj-angles.adamghill.com\/en\/latest\/\">dj-angles<\/a>.<\/p>\n<p>Whether this makes it into main and get&#x27;s released is not the point, to me I am simply amazed that an idea I penned down, got a thumbs up from someone who took action to implement it so quickly! To me, this is one sign of a healthy community. While Django is slow moving in terms of the core codebase (and for good reason), the third-party package ecosystem is thriving and today I simply wanted to celebrate that fact and the interaction with Adam!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-224","title":"Error Boundaries in Django - 2","summary":"A follow up to the idea","date_modified":"2024-11-18T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#templates","#error_boundary"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-223","content_html":"<p>I have been further pondering the idea of what a partial view would represent. I think something clicked in my head when I wrote this scenario for a url mapping the other day:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\">    path<span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;\/dashboard&#x27;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token punctuation\">[<\/span>views<span class=\"token punctuation\">.<\/span>index<span class=\"token punctuation\">,<\/span> views<span class=\"token punctuation\">.<\/span>new_task<span class=\"token punctuation\">,<\/span> views<span class=\"token punctuation\">.<\/span>list_tasks<span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">,<\/span> name<span class=\"token operator\">=<\/span><span class=\"token string\">&#x27;dashboard&#x27;<\/span><span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>This would mean instead of a dashboard view combining the logic for each of these tasks (creating a new task, listing tasks and providing overall task stats), we could have a single view for each thing, that could either be used in a separate view or combined into an existing view.<\/p>\n<p>Our views.py could look like:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token keyword\">def<\/span> <span class=\"token function\">index<\/span><span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    context <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">{<\/span>\n        <span class=\"token string\">&#x27;completed_tasks&#x27;<\/span><span class=\"token punctuation\">:<\/span> Task<span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span><span class=\"token builtin\">filter<\/span><span class=\"token punctuation\">(<\/span>status<span class=\"token operator\">=<\/span><span class=\"token string\">&#x27;completed&#x27;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">.<\/span>count<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n        <span class=\"token string\">&#x27;tasks_today&#x27;<\/span><span class=\"token punctuation\">:<\/span> Task<span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span><span class=\"token builtin\">filter<\/span><span class=\"token punctuation\">(<\/span>due<span class=\"token operator\">=<\/span>now<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">.<\/span>today<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">)<\/span>\n        <span class=\"token string\">&#x27;overdue&#x27;<\/span><span class=\"token punctuation\">:<\/span> Tasks<span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span><span class=\"token builtin\">filter<\/span><span class=\"token punctuation\">(<\/span>due__lt<span class=\"token operator\">=<\/span>now<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">.<\/span>today<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">)<\/span>\n    <span class=\"token punctuation\">}<\/span>\n    <span class=\"token keyword\">return<\/span> render<span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">,<\/span> <span class=\"token string\">&#x27;index.html&#x27;<\/span><span class=\"token punctuation\">,<\/span> context<span class=\"token punctuation\">)<\/span>\n\n<span class=\"token keyword\">def<\/span> <span class=\"token function\">new_task<\/span><span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    <span class=\"token keyword\">if<\/span> request<span class=\"token punctuation\">.<\/span>method <span class=\"token operator\">==<\/span> <span class=\"token string\">&#x27;POST&#x27;<\/span><span class=\"token punctuation\">:<\/span>\n        form <span class=\"token operator\">=<\/span> TaskForm<span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">.<\/span>POST<span class=\"token punctuation\">)<\/span>\n        <span class=\"token keyword\">if<\/span> form<span class=\"token punctuation\">.<\/span>is_valid<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n            form<span class=\"token punctuation\">.<\/span>save<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n            <span class=\"token keyword\">return<\/span> HttpResponseRedirect<span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;index&#x27;<\/span><span class=\"token punctuation\">)<\/span>\n    <span class=\"token keyword\">else<\/span><span class=\"token punctuation\">:<\/span>\n        form <span class=\"token operator\">=<\/span> TaskForm<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n    <span class=\"token keyword\">return<\/span> render<span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">,<\/span> <span class=\"token string\">&#x27;new_task.html&#x27;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token punctuation\">{<\/span><span class=\"token string\">&#x27;form&#x27;<\/span><span class=\"token punctuation\">:<\/span> form<span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span>\n\n<span class=\"token keyword\">def<\/span> <span class=\"token function\">list_tasks<\/span><span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    <span class=\"token keyword\">return<\/span> render<span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">,<\/span> <span class=\"token string\">&#x27;list_tasks.html&#x27;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token punctuation\">{<\/span><span class=\"token string\">&#x27;tasks&#x27;<\/span><span class=\"token punctuation\">:<\/span> Task<span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span><span class=\"token builtin\">all<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>Then our expanded URLs could look like:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\">urlpatterns <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">[<\/span>\n    path<span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;\/dashboard&#x27;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token punctuation\">[<\/span>views<span class=\"token punctuation\">.<\/span>index<span class=\"token punctuation\">,<\/span> views<span class=\"token punctuation\">.<\/span>new_task<span class=\"token punctuation\">,<\/span> views<span class=\"token punctuation\">.<\/span>list_tasks<span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">,<\/span> name<span class=\"token operator\">=<\/span><span class=\"token string\">&#x27;dashboard&#x27;<\/span><span class=\"token punctuation\">)<\/span>\n    path<span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;\/new&#x27;<\/span><span class=\"token punctuation\">,<\/span> views<span class=\"token punctuation\">.<\/span>new_task<span class=\"token punctuation\">,<\/span> name<span class=\"token operator\">=<\/span><span class=\"token string\">&#x27;new_task&#x27;<\/span><span class=\"token punctuation\">)<\/span>\n    path<span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;\/list&#x27;<\/span><span class=\"token punctuation\">,<\/span> views<span class=\"token punctuation\">.<\/span>list_tasks<span class=\"token punctuation\">,<\/span> name<span class=\"token operator\">=<\/span><span class=\"token string\">&#x27;list_tasks&#x27;<\/span><span class=\"token punctuation\">)<\/span>\n<span class=\"token punctuation\">]<\/span>\n<\/code><\/pre>\n<p>There are a few large unresolved problems with this design. First is to ensure solutions don&#x27;t involve too much magic to the developer. The second major problem is to with rendering templates which has two sub issues, first is combining the rendered responses correctly (<a href=\"https:\/\/indiehackers.social\/deck\/@boxed@mastodon.social\/113477010817248034\">boxed<\/a> has a suggestion of a middleware which seems reasonable on first glance). The second sub issue is in the templates itself, how would we know to render something as a response inside another or as a complete response by itself.<\/p>\n<p>I&#x27;m going to ponder the design of that this weekend, perhaps it&#x27;s a post for next week.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-223","title":"Partial Views - Part 2","summary":"Further exploring the idea of breaking down views","date_modified":"2024-11-15T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#idea"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-222","content_html":"<p>I wonder if Django could borrow the <a href=\"https:\/\/github.com\/bvaughn\/react-error-boundary#readme\">Error Boundary<\/a> component concept from React? Now I know it won&#x27;t map exactly as the Error Boundary in React is a client side thing and Django is server side. However I wonder if a similar concept could be applied when rendering templates.<\/p>\n<p>If your not familiar with the Error Boundary, my understanding is that it is a component which limits how far an error can propagate within a React App. This means only a part of the page will error, leaving the rest of page to render successfully.<\/p>\n<p>If this were in Django I would imagine the API to be exposed through the <code>includes<\/code> or <code>block<\/code> statements (or perhaps a new template tag?). Below is an example of how it could look.<\/p>\n<pre class=\"language-html\"><code class=\"language-html\">\n{% block my_content %}\n    <span class=\"token comment\">&lt;!-- Normal template goes here --&gt;<\/span>\n{% blockerror my_content%}\n    <span class=\"token comment\">&lt;!-- if the above content errors then show this content --&gt;<\/span>\n{% endblock my_content %}\n\n<\/code><\/pre>\n<p>or with includes it could be a special variable name? (I&#x27;m less sold on this version)<\/p>\n<pre class=\"language-html\"><code class=\"language-html\">{% includes &quot;myapp\/snippets\/row.html&quot; with context_vars=here error_fallback=&quot;This errored&quot; %}\n<\/code><\/pre>\n<p>Essentially the aim is to have partial errors when rendering a template in a standard manner. This needs testing in a package to see if it&#x27;s even feasible, but I feel like it might be worth exploring rather than have the whole page exploding. This becomes even more relevant when used with HTMX or similar.<\/p>\n<p>Or I am way off the mark? Let me know.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-222","title":"Error boundaries in Django Templates","summary":"Borrowing an idea from React","date_modified":"2024-11-14T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#idea","#templates","#package"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-221","content_html":"<p>Today while pondering my ideas from yesterday, I realised that I am thinking of something similar to other more advanced templating\/view packages (eg <a href=\"https:\/\/www.django-unicorn.com\/\">django-unicorn<\/a>). However I don&#x27;t fully know as I have yet to build anything with django-unicorn or <a href=\"https:\/\/github.com\/EmilStenstrom\/django-components\">any<\/a> <a href=\"https:\/\/django-viewcomponent.readthedocs.io\/en\/latest\/\">of<\/a> <a href=\"https:\/\/github.com\/Xzya\/django-web-components\">the<\/a> <a href=\"https:\/\/django-cotton.com\/\">other<\/a> <a href=\"https:\/\/django-bird.readthedocs.io\/en\/latest\/index.html\">packages<\/a> that exist in the space to know whether my idea matches any of them.<\/p>\n<p>So this brings me to a few questions, which I would love to hear answers for.<\/p>\n<ol>\n<li>What do you build to experiment with a new package or set of packages?<\/li>\n<\/ol>\n<p>As a freelancer I&#x27;m hesistant to try new packages unless I know fairly well it&#x27;s going to fit the need of the client and not leave the codebase with 10 packages half used.<\/p>\n<ol start=\"2\">\n<li>\n<p>This is more of a personal one and dependant on the above. How do you find the time to do this?<\/p>\n<\/li>\n<li>\n<p>Does this come at a cost at building packages to contribute back to the community?<\/p>\n<\/li>\n<\/ol>\n<p>One idea that comes to mind is to have a simple(ish) project that has a well known core (eg a to-do list project) which I can then branch off and rebuild aspects to trial different packages.<\/p>\n<p>Do let me know your answers!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-221","title":"Question: How do you experiment and explore new packages..","summary":"I am stuck in wanting to explore existing ideas or new ones","date_modified":"2024-11-13T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#idea","#packages","#experiments"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-220","content_html":"<p>I noted the phrase &quot;Partial Views&quot; down the other day after a conversation on Discord about the desire for patterns of use with HTMX in Django. I&#x27;m not sure what the end result of this would be and perhaps captures more of a future idea for a library than a core function.<\/p>\n<p>I think what the idea I&#x27;m trying to wrap my head around is perhaps having a separate view function that returns a partial bit of an overall webpage, without needing to have extra conditional logic.<\/p>\n<p>This idea also reminds me of perhaps a pattern of vertical micro-frontends for Django, what would this look like? Most likely it would be async in some fashion, but here it&#x27;s limiting the error boundary to particular section on the page in a a similar manner to React does with it&#x27;s ErrorBoundary API. I would imagine async would need to be involved to enable this in some fashion to make this work effectively.<\/p>\n<p>Perhaps the interface could look like mapping a list to a single URL?<\/p>\n<pre class=\"language-py\"><code class=\"language-py\">    path<span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;\/dashboard&#x27;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token punctuation\">[<\/span>views<span class=\"token punctuation\">.<\/span>index<span class=\"token punctuation\">,<\/span> views<span class=\"token punctuation\">.<\/span>new_task<span class=\"token punctuation\">,<\/span> views<span class=\"token punctuation\">.<\/span>list_tasks<span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">,<\/span> name<span class=\"token operator\">=<\/span><span class=\"token string\">&#x27;dashboard&#x27;<\/span><span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>With then the first view returning a whole template and the others returning partial templates? The beauty here is the simplicity of reusing functions. I imagine we could have these as methods on a class and then decoratored?.<\/p>\n<p>I do realise that no view is really ever partial, as they would all take a request and return a response. Just some ideas knocking around my head, pushing where these Django could go.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-220","title":"Partial Views","summary":"A possible partner to partial templates","date_modified":"2024-11-12T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#idea"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-219","content_html":"<p>This week I need to implement a new feature for a client. The client allows users to add recipients to their platform and when doing so under certain conditions we need to make a call out to a third-party API, which may return some warnings.<\/p>\n<p>What&#x27;s interesting here is that the page is already setup with a Django Form and works well, however these new warnings which come back are what I would term &#x27;soft&#x27; validation fails, a user can choose to ignore them and proceed to add a recipient. That said Django only has two possible validation states for a form (valid and invalid), so adding a third state is going to be an interesting problem to solve. Initially I&#x27;m thinking maybe some state in the session to acknowledge bypassing some of the validation.<\/p>\n<p>Have you encountered a similar issue? I&#x27;m interested if you have ever encountered a similar thing. Later this week I will share the solution I come up with!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-219","title":"Soft validation in Django forms","summary":"How is this going to work...","date_modified":"2024-11-11T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#forms","#validation"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-218","content_html":"<p>So a recent discovery is that the existing teams with Django are under the direction of Steering Council. Therefore one thing I would like the new Steering Council to achieve (or set in motion) is clearer guidelines for any new teams &amp; roles and documenting those guidelines for existing teams &amp; roles.<\/p>\n<p>What do I mean by guidelines? Well <a href=\"https:\/\/github.com\/django\/deps\/blob\/main\/final\/0010-new-governance.rst#new-role-merger\">DEP 10<\/a> is a good start for some of the guidelines, but the honestly language is difficult in DEP10 and I prefer the language &amp; questions proposed in the <a href=\"https:\/\/github.com\/django\/dsf-working-groups\/blob\/main\/template.md\">Working Group template<\/a>. The goal here is to establish some clearer processes around teams that are more explicitly documented.<\/p>\n<p>One key focus to me would be clearly defining on-ramps and off-ramps to roles and teams. Almost everyone is a volunteer in the community and therefore while roles need to be filled, noone should feel trapped in a role. Documenting the off-ramps allows for someone to step away and the on-ramps allow new people to get easily put themselves forward.<\/p>\n<p>A bigger picture thought is that to map out pathways in the community that potentially map a contributor gaining certain experience which enables them to further their career. This would take fair amount of time to complete, but a worthy goal.<\/p>\n<p>A final note that this would include the Steering Council itself, the clearer we are, the more we get people coming forward.<\/p>\n<p>Final, final note it turns out I&#x27;m eligible for the Steering Council, so expect to see my name on the nominations list!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-218","title":"Steering Council Wishlist - Part 2","summary":"I missed one wish...","date_modified":"2024-11-08T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#teams","#steering-council"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-217","content_html":"<p>While pondering the idea of promoting some third-party packages to be canonical or &#x27;official&#x27;, with the intention of creating smoother on-ramps for certain features that ought to be considered defaults in Django. We come across the issue of if a promoted package no longer meeting the needs of the community, becomes unmaintained, or another package becoming a preference.<\/p>\n<p>The classic example here is with Django Rest Framework thought to be the default for building APIs, but recently django-ninja has become a popular alternative.<\/p>\n<p>Therefore the idea of official packages having terms similar to that of board members, maybe &#x27;package sponsorship&#x27; would be a better name. The idea is rather simple, that a package only get&#x27;s promoted for a fixed period of time before that decision needs to be reevaluated, allowing for alternative packages to nominiate themselves.<\/p>\n<p>For this to work this requires us as community to defined the features (or batteries) that we want to include officially in the documentation (and website), and then have a single reference to the actual package in the docs so updates are quick to do, without wondering if we missed a reference in a page somewhere.<\/p>\n<p>It&#x27;s not a perfect solution, but I think one that manages the tensions best so far, assuming the community goes this route.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-217","title":"Could canonical packages have terms","summary":"Yet more elections?","date_modified":"2024-11-07T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#packages","#contrib"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-216","content_html":"<p>I have have said this <a href=\"\/100-words\/day-140\">before<\/a>, but I was reminded of it yesterday, just use the defaults provided by Django for primary keys. There is the <code>unique<\/code> kwarg on any field if you need another identifier.<\/p>\n<p>Here are some examples I have seen in the wild of other primary key generated (all of which have generated issues at some point)<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token keyword\">def<\/span> <span class=\"token function\">gen_id_small<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    t <span class=\"token operator\">=<\/span> <span class=\"token builtin\">int<\/span><span class=\"token punctuation\">(<\/span>time<span class=\"token punctuation\">.<\/span>time<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">-<\/span> <span class=\"token number\">1615005089<\/span>\n    u <span class=\"token operator\">=<\/span> random<span class=\"token punctuation\">.<\/span>SystemRandom<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">.<\/span>getrandbits<span class=\"token punctuation\">(<\/span><span class=\"token number\">4<\/span><span class=\"token punctuation\">)<\/span>\n    <span class=\"token keyword\">return<\/span> <span class=\"token punctuation\">(<\/span>t <span class=\"token operator\">&lt;&lt;<\/span> <span class=\"token number\">4<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">|<\/span> u\n\n\n<span class=\"token keyword\">def<\/span> <span class=\"token function\">gen_id_large<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    t <span class=\"token operator\">=<\/span> <span class=\"token builtin\">int<\/span><span class=\"token punctuation\">(<\/span>time<span class=\"token punctuation\">.<\/span>time<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">-<\/span> settings<span class=\"token punctuation\">.<\/span>BASE_TIME\n    u <span class=\"token operator\">=<\/span> random<span class=\"token punctuation\">.<\/span>SystemRandom<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">.<\/span>getrandbits<span class=\"token punctuation\">(<\/span><span class=\"token number\">16<\/span><span class=\"token punctuation\">)<\/span>\n    <span class=\"token keyword\">return<\/span> <span class=\"token punctuation\">(<\/span>t <span class=\"token operator\">&lt;&lt;<\/span> <span class=\"token number\">16<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">|<\/span> u\n<\/code><\/pre>\n<p>The above functions are set as callables on the id field, this leads to clashes if too many objects are created at same time, additionally it does computation in python and not in the DB which is always going to be problematic.<\/p>\n<p>Also this one from an old job doesn&#x27;t suffer from clashes, but makes it hard to use with other libraries that assume a numeric primary key. I definitely hit this once or twice when trying to do something on this codebase.<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token keyword\">from<\/span> shortuuidfield <span class=\"token keyword\">import<\/span> ShortUUIDField\n<span class=\"token keyword\">class<\/span> <span class=\"token class-name\">Model<\/span><span class=\"token punctuation\">(<\/span>models<span class=\"token punctuation\">.<\/span>Model<span class=\"token punctuation\">)<\/span>\n    <span class=\"token builtin\">id<\/span> <span class=\"token operator\">=<\/span> ShortUUIDField<span class=\"token punctuation\">(<\/span>auto<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">,<\/span> primary_key<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>Do you have any code scars that should be avoided in future?<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-216","title":"How not to create primary keys in Django","summary":"Honestly just use the default","date_modified":"2024-11-06T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#ORM","#primary_keys"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-215","content_html":"<p>Yesterday I wrote about a challenge I faced at work last week and wondered if it could be optimised using the ORM rather than a simple python solution.<\/p>\n<p>My first response was from Danielle on <a href=\"https:\/\/www.linkedin.com\/feed\/update\/urn:li:activity:7259236545990160384?commentUrn=urn%3Ali%3Acomment%3A%28activity%3A7259236545990160384%2C7259319053540843520%29&amp;dashCommentUrn=urn%3Ali%3Afsd_comment%3A%287259319053540843520%2Curn%3Ali%3Aactivity%3A7259236545990160384%29\">Linkedin<\/a> suggesting that I use <code>order_by<\/code> with multiple fields. This is a valid suggestion on any sane codebase, but unfortunately this doesn&#x27;t work since a previous developer decided to implement custom primary keys that I simply don&#x27;t trust (they don&#x27;t auto increment and have a tendency to clash).<\/p>\n<p>The other response was from Tim on <a href=\"https:\/\/indiehackers.social\/deck\/@CodenameTim@fosstodon.org\/113425842623705781\">Mastodon<\/a> suggesting that I could do it in two queries (the fetch of data then <code>bulk_update<\/code>). This would be an improvement of reducing the N times I save each time to a single trip to the database, but I think I would still need to loop over each of the items to calcuate the new times.<\/p>\n<p>I hope to revisit this as some point to work out if I can do the time increments at the database level rather than in python.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-215","title":"Efficient Django Query Challenge - Responses","summary":"Responses to the challenge I pondered yesterday ","date_modified":"2024-11-05T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#ORM","#query"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-214","content_html":"<p>Last week I was tasked with implementing a sort functionality on one of my clients projects. The sorting was to be done on a time field associated with an item. However, in the current state, many existing items would all hold the same time leading to undefined behaviour when swapping items around to change the order.<\/p>\n<p>Therefore to solve this undefined behaviour I updated the time to reflect the originial order before doing any swaps. This means a queryset with 4 items in it for a time of say 13:00, would get updated to 13:00, 13:01, 13:02, 13:03. I tried to do this as a query to begin with before resorting to a python implementation below:<\/p>\n<pre class=\"language-python\"><code class=\"language-python\">ODD_SORTING <span class=\"token operator\">=<\/span> <span class=\"token number\">3<\/span>\n<span class=\"token keyword\">if<\/span> unsorted_queryset<span class=\"token punctuation\">.<\/span>count<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">&gt;=<\/span> ODD_SORTING<span class=\"token punctuation\">:<\/span>\n    dup_times <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">(<\/span>\n        unsorted_queryset<span class=\"token punctuation\">.<\/span>values<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;time&quot;<\/span><span class=\"token punctuation\">)<\/span>\n        <span class=\"token punctuation\">.<\/span>annotate<span class=\"token punctuation\">(<\/span>Count<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;time&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">)<\/span>\n        <span class=\"token punctuation\">.<\/span><span class=\"token builtin\">filter<\/span><span class=\"token punctuation\">(<\/span>time__count__gte<span class=\"token operator\">=<\/span>ODD_SORTING<span class=\"token punctuation\">)<\/span>\n    <span class=\"token punctuation\">)<\/span>\n    <span class=\"token comment\"># if we have 3 or more items at the same time then set the ordering manually<\/span>\n    <span class=\"token keyword\">if<\/span> dup_times<span class=\"token punctuation\">.<\/span>exists<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        <span class=\"token comment\"># Here we need to manually create an ordering for all the items with<\/span>\n        <span class=\"token comment\"># the same time<\/span>\n        <span class=\"token keyword\">for<\/span> dtime <span class=\"token keyword\">in<\/span> dup_times<span class=\"token punctuation\">:<\/span>\n            <span class=\"token keyword\">for<\/span> idx<span class=\"token punctuation\">,<\/span> item <span class=\"token keyword\">in<\/span> <span class=\"token builtin\">enumerate<\/span><span class=\"token punctuation\">(<\/span>unsorted_queryset<span class=\"token punctuation\">.<\/span><span class=\"token builtin\">filter<\/span><span class=\"token punctuation\">(<\/span>time<span class=\"token operator\">=<\/span>dtime<span class=\"token punctuation\">[<\/span><span class=\"token string\">&quot;time&quot;<\/span><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n                item<span class=\"token punctuation\">.<\/span>time <span class=\"token operator\">=<\/span> item<span class=\"token punctuation\">.<\/span>time<span class=\"token punctuation\">.<\/span>replace<span class=\"token punctuation\">(<\/span>minute<span class=\"token operator\">=<\/span>item<span class=\"token punctuation\">.<\/span>time<span class=\"token punctuation\">.<\/span>minute <span class=\"token operator\">+<\/span> idx<span class=\"token punctuation\">)<\/span>\n                item<span class=\"token punctuation\">.<\/span>save<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>I&#x27;m open to any suggestions to make this more efficient, it works but I would prefer to remove those for loops. I think something could be done with a window function to get the row number which then forms the offset, but I could be completely wrong having not needed window functions myself before.<\/p>\n<p>Like I said, do let me know how you would improve this.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-214","title":"Efficient Django Query Challenge","summary":"I wonder if the ORM can handle this... I resorted to python","date_modified":"2024-11-04T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#ORM","#query"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-213","content_html":"<p>A clarifying post on terminology today. Django is described as having &quot;batteries included&quot;, this generally means tha common tasks are easy to do and have been thought about, normally with some code of some form already written. The package ecosystem provides most of these batteries, while Django provides the core batteries we need to build web applications.<\/p>\n<p>The clarifying point here is that when we talk about adding batteries into Django, the first thing that normally comes to mind is the desire to add more code in the form of packages into core. This desire and assumption is dangerous and not within the grain of Django (thanks Carlton!).<\/p>\n<p>Batteries to me are features or specifications. The closest thing you get to functioning code is an API specification of how you expect the battery to work when interacting with the rest of the project. More often it would take the form of a written specification.<\/p>\n<p>Packages are the most common implementation of a Battery whether in core or outside as a separate package. But a Battery could easily be written in another language (eg ASGI\/WSGI servers) or could be a remote API with minimal python included.<\/p>\n<p>Why make this distinction? It&#x27;s fairly simple in my mind, before any talk of what of how packages get promoted (in whatever form) into core or elevated in the documentation, we first need to establish the batteries that we want included. This obviously changes over time, but in order to maintain the stability of Django I think the distinction is crucial to conversation around packages and features for Django.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-213","title":"Django Batteries versus packages","summary":"What is in a name?","date_modified":"2024-11-01T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#contributing","#packages"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-212","content_html":"<p>Yesterday I attended my first DSF office hours and I throughly enjoyed it. There was a nice amount of Django community related chat between those that attended. It seems to be that perfect mix of doing related work as well as the watercooler style chat if Django was a company and not an open source community.<\/p>\n<p>It also provided me with the space to finally push the first draft of my Community Working Group proposal over the line and created a PR for it. You can read it <a href=\"https:\/\/github.com\/django\/dsf-working-groups\/pull\/23\">here<\/a>, please do make comments, suggestions and ideas. Also put yourself forward if you want to actively improved the online spaces the community uses. This is primarily the Discord and Forum, but could include other spaces in future, but there is also plenty of ideas for these spaces right now.<\/p>\n<p>What&#x27;s lacking is people to get involved... could that be you?<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-212","title":"My first DSF Office Hours","summary":"Getting some Django community work done","date_modified":"2024-10-31T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#community","#dsf"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-211","content_html":"<p>I&#x27;m getting two tasks done today, writing this as preparing a challenge for my Cub Scout pack. This post is going to me exploring the idea as I need to get my thought down on paper. A small bit of context, I help lead a cub pack and we are currently doing the money skills badge, part of which involves teaching them about budgets and how different people use money in different ways (cultural, religous, adverts etc).<\/p>\n<p>The core idea was to have a multi-week challenge that lasts until the Christmas break. Each week we would award points to the cubs, this is their income. The would pay fines for not having correct uniform, not signing in or forgetting their water bottle. They can earn points by being helpful or for good choices.<\/p>\n<p>Next is spending or giving points away. They can spend points on choosing a game, donating points to another six (a group within the pack) or perhaps bidding on getting first pick in an activity.<\/p>\n<p>Each of these points could be a small focus each week which leads to a larger expenditure at the end of the term. Ultimately what is the point of a budget? It&#x27;s to enable you to have disposable income to spend on a holiday or other luxury items. So at the end of term we need them to hit a target point count to unlock a reward or we have a sweet shop in our last session to spend the points on. Those who have budgeted will have more, those who have spent less will have more at the end.<\/p>\n<p>Thanks for the off-topic subject today! I think I&#x27;m in a better place to craft a detailed plan.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-211","title":"Teaching budgeting to children","summary":"Life skills for Cub Scouts","date_modified":"2024-10-30T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#scouts","#budgeting","#money_skills"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-210","content_html":"<p>Voting opened for the DSF board elections today! As a DSF Individual member it is a privilege that I am able to cast a vote for those who have put themselves forward. We have 21 candidates to choose from to fill four board positions. The candidates from across the globe representing the diverse nature of the Django community.<\/p>\n<p>Today is simply an appreciation to those 21 for putting themselves forward and being willing to sacrifice some of their time for the next two years to move the Django community forward, it&#x27;s not a light commitment to take on and I am sure those elected will be up to the task!<\/p>\n<p>If you&#x27;re a DSF individual member please do take the time to vote with 21 candidates it&#x27;s going to take more than a single coffee break to read all the statements!<\/p>\n<p>Finally if you are involved in the Django community and think you qualify for DSF individual membership then <a href=\"https:\/\/www.djangoproject.com\/foundation\/individual-members\/\">review the criteria<\/a> and nominate yourself to allow you to take part in defining the future of Django.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-210","title":"DSF Board Elections open today","summary":"Congrats to all those who put themselves forward","date_modified":"2024-10-29T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#dsf","#community"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-209","content_html":"<p>Bugs and errors are a natural part of any piece of software, someone who tells you otherwise is generally not to be trusted. What is key is being notified of when they happen and getting as much context around the error in order to fix it quickly.<\/p>\n<p>To begin with Django has some built in mechanisms to report errors, first is printing to the terminal and logging. These are great options for local development and historically going back to help find the cause of the error, however they do require you to be staring at the terminal all day once you deploy your project. Thankfully Django provides another option through the <code>ADMINS<\/code> setting. This is a list of tuples which has the emails of anyone who wants to recieve an email anytime an exception occurs. This is a good free first step, but can lead to your inbox getting clogged and the information you get from the email is good, but could be better.<\/p>\n<p>My recommended setup is to use a service like <a href=\"https:\/\/sentry.io\/welcome\/\">Sentry<\/a>, <a href=\"https:\/\/www.bugsnag.com\/\">BugSnag<\/a> or <a href=\"https:\/\/rollbar.com\/\">Rollbar<\/a> to capture exceptions in a deloyed production environment. These tools can capture alot of information, most notably the local variables for each frame of the stacktrace. This allows a developer to really dig into the exception and understand what&#x27;s going on in the production environment. They also have the benefit of capturing errors from other tech stacks (web or mobile apps) to allow tracing bugs from the client as well as the server.<\/p>\n<p>By default I personally go for Sentry any time I start a project, but the other services are just as good, and I tend to not set the email settings as I like my inboxes clear!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-209","title":"Exception handling in Django","summary":"What options are available?","date_modified":"2024-10-28T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#exceptions"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-208","content_html":"<p>One of the tedious (and time consuming) parts of testing or creating a demo environment is creating realistic test data, that remains up to date with the latest code and database schema.<\/p>\n<p>As part of this new project I am about to start, the system will be consuming a fair few third-party API&#x27;s to create the product. I would like to create a demo environment from day zero as this will have a couple of benefits, namely illustrating the value to leads and letting us iterate on the frontend features faster.<\/p>\n<p>I&#x27;m wondering if a package exists to accomplish this goal? If not, then it&#x27;s going to be on my todo list fairly soon. I&#x27;m there will be a script element to run generate data and have it saved in files or do some code generation that will generate factory methods to create the necessary data by creating model instances or call the APIs to get the data and then save that data, or perhaps code gen to create demo API clients.<\/p>\n<p>As I&#x27;m writing this, there are two distinct problems, first is data held in the database, this is more defined as libraries exist to help with this. The second problem is data coming from API&#x27;s. Here there is more custom code, unless OpenAPI can help (assuming the third-parties using Open API spec)? I&#x27;m not sure what exists in this space, I&#x27;ll write when I find out! Or could the data be saved as I&#x27;m doing development?<\/p>\n<p>Lots of questions, that will have answers in the coming months. This will be a tool I create for this project. Let me know if you have any tools for demo data creation or is it all too specific to a single project to be useful?<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-208","title":"Django or Python package to save API responses","summary":"Creating test or demo data easily","date_modified":"2024-10-25T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#package","#idea"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-207","content_html":"<p>If we take the large assumption that being an open source maintainer is a financially viable option to make a living as a choice (while some do succeed in this it&#x27;s not commonplace), I wonder what other aspects of support maintainers would need?<\/p>\n<p>Let&#x27;s start with the word maintainer, this is traditionally limited to those who write the code, but in a large community this could include any community member that &#x27;maintains&#x27; something, this could be teaching content, conference organisers, moderators, anyone who spends their own time giving back to the community.<\/p>\n<p>Beyond financial support, I wonder what other support we could provide each other as a community? For me it&#x27;s supporting other&#x27;s wellbeing, be it physical, mental, emotional, relational or spiritual. These aspects form the heart of any functional community. These are broad categories each with lots of potential ways and ideas for us to support each other.<\/p>\n<p>What&#x27;s brilliant about these other aspects is that the financial aspect of implementing them is low or zero, it takes time (which does cost us), but I find we always gain something far deeper.<\/p>\n<p>To that end, if anyone want to chat or would like some support (disclaimer I&#x27;m not a professional therapist etc!), then feel free to reach out to me via email or in a DM referrencing this post.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-207","title":"How do we support for each other in the Django community?","summary":"If financial pressures were not an issue, what is next?","date_modified":"2024-10-24T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#community","#maintainers"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-206","content_html":"<p>I have been mulling over more of the contrib posts from the last week or so and also the process that would want to exist to qualify which packages get elevated or the blessed status to exist within core. We will need a rubric or framework to evaluate them.<\/p>\n<p>Today I remembered that other foundations have achieved this for different project styles, most notably the Apache Foundation and CNCF are ones that come to mind when projects have stages (eg, Sandbox, Incubator, Graduated, Retired). Moving from one stage to the next requires a process which is clearly documented along with expectation from the projects and the vice versa from the CNCF providing services to the projects.<\/p>\n<p>A starting point to me would be to borrow the <a href=\"https:\/\/github.com\/cncf\/toc\/blob\/main\/process\/README.md\">process<\/a> from the CNCF and adapt it to our community. I feel like their are a few pieces of this puzzle in existance already (or have recently started), namely djangopackages.org has data on existing packages and some scoring on these packages to start a rubric. Django-commons has recently started which could be a possibly location for packages at certain stage in the process or it could be used as a template to learn from. Finally there is a proposed packages working group which could be the official place where this process gets discussed and finalised.<\/p>\n<p>The key to me is to start something in this area and something that the next Steering Council could lead on with the relevant working groups.<\/p>\n<p>That&#x27;s it for today, more ponderings tomorrow.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-206","title":"Django Package maturity levels","summary":"Further ponderings on the package ecosystem","date_modified":"2024-10-23T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#contribution","#cncf","#packages"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-205","content_html":"<p>I performed a recent informal survey on <a href=\"https:\/\/indiehackers.social\/deck\/@nanorepublica\/113311008714701602\">Mastodon<\/a> about migrating the staticfiles app out of contrib into core. It was generally a positive response with some good questions and considerations to take into account. Today however I want to focus on if the community agreed that this was a good idea, what options do we have available?<\/p>\n<p>Option 1: Move the directory wholesale and update imports to be <code>django.staticfiles<\/code> instead of <code>django.contrib.staticfiles<\/code>. This is certainly the easiest change, but one that is instant and a breaking change, which isn&#x27;t ideal. (Obviously this would be more of a copy\/paste scenario that follows the deprecation policy!)<\/p>\n<p>Option 2: Identify the most appropriate home for each section of code within the <code>staticfiles<\/code> app. If we work through the app, then management commands could be moved into the appropriate directory, similarly with checks, handlers, storage and testing. What is less clear is where urls and views would be located, they are the most normal app blocks of code to exist in the app as they don&#x27;t easily belong in <code>django.urls<\/code>, for example, as that module is about housing the code that creates and managed url routing instead of creating actual urls to be used in a project.<\/p>\n<p>Generally I would prefer option two if it were an accepted change as the deprecation would be a smoother ride to my mind, over a big bang change. Do you agree? Is there a third or forth option I&#x27;ve not thought of?<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-205","title":"Options for migrating the staticfiles app into core","summary":"What would it take to migrate a contrib app into Django","date_modified":"2024-10-22T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#contribution","#migration"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-204","content_html":"<p>It&#x27;s fairly well known in the community that signals are a bit of anti-pattern. They make code harder to reason about and generally there are other approaches that are clearer and easier to maintain. The time to use them is when you need to invert the control of some logic and are being particular about logical dependencies in your django project.<\/p>\n<p>To me the other reason they are a bit of an anti-pattern is that most projects only use half of the functionality offered by only hooking into the default ones within Django itself, which while useful in certain scenarios are too generic to be properly useful. The power with signals comes when you define both the signal and reciever function. (Side note, most recievers are defined in <code>signals.py<\/code> in most projects, however I prefer to defined them in <code>recievers.py<\/code>).<\/p>\n<p>Defining custom signals allow you to define business specific signals which get fired not in a generic way, but under specific circumstances and now with in the introduction of async signals could allow for a message-passing based Django project. You can do 1-2-1 messages, fanout based message passing within signals and deduplication. This is a project I want to try one day, while this would go against the norms, I would be interested to see it play out.<\/p>\n<p>Finally, here is a quick reminder about how to create a custom signal:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token keyword\">import<\/span> django<span class=\"token punctuation\">.<\/span>dispatch\n\ntask_completed <span class=\"token operator\">=<\/span> django<span class=\"token punctuation\">.<\/span>dispatch<span class=\"token punctuation\">.<\/span>Signal<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>which then get&#x27;s triggered as follows:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\">\n<span class=\"token keyword\">from<\/span> <span class=\"token punctuation\">.<\/span>signals <span class=\"token keyword\">import<\/span> task_completed\n<span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span>\n\ntask_completed<span class=\"token punctuation\">.<\/span>send<span class=\"token punctuation\">(<\/span>sender<span class=\"token operator\">=<\/span><span class=\"token string\">&#x27;mysender&#x27;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token operator\">*<\/span>args<span class=\"token punctuation\">,<\/span> <span class=\"token operator\">**<\/span>kwargs<span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>Then you hook in a reciever callback function as usual...and that&#x27;s it!<\/p>\n<p>Have you used custom signals? Let me know, I curious to know where they crop up.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-204","title":"Custom Signals in Django","summary":"Opinion piece incoming - when to use signals","date_modified":"2024-10-21T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#signals"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-203","content_html":"<p>I know DSF board nominations are open right now and there have been a few <a href=\"https:\/\/gist.github.com\/sarahboyce\/68ffaaeae24d2501cf27a914f77fb97c\">posts<\/a> about <a href=\"https:\/\/www.better-simple.com\/django\/2024\/10\/13\/dsf-initiatives-i-would-like-to-see\/\">initiatives<\/a> <a href=\"https:\/\/noumenal.es\/posts\/dsf-board-election\/N8W\/\">others<\/a> would like to see. I generally agree with what has been said in those posts and have nothing to add.<\/p>\n<p>However the Steering council elections for the next release cycle are going to be happening soon, therefore I would like to focus on what I think is required from the next Steering Council.<\/p>\n<p>First up is a roadmap, while DEP 12 states that the Council shouldn&#x27;t create this by themselves, I would like them to provide focus to the community on what the next big features Django should be working on to bring into core. There has been plenty of dicussion this year about potential big features and items for a roadmap. I wouldn&#x27;t expect this roadmap to have hard dates attached to it, but I expect it to set the direction of travel and for the community to be involved in the approval for it.<\/p>\n<p>Second is clarifying the process for third-party packages being promoted to a &#x27;first-party&#x27; package or even into core. DEP 2 already exists about Experimental API&#x27;s within core. A good first step in this regard would be consider which third-party packages need promotion within the docs to be the default choice for something core doesn&#x27;t currenyly provide. This requires some discussion to pick the holes (which I have started) and then to research the packages available and pick the final one for now. Finally it would be establishing a review process to check that the package still meets the needs in future.<\/p>\n<p>Third I think there needs to be some consideration of what in core is no longer required, what aspects of the framework are feature complete, which aspects could be deprecated and which are still require active development. I say this with the awareness of the deprecation policy, but also that if we are to move forward and potentially add new features, how do we lighten the maintenance burden.<\/p>\n<p>Finally, there is a consideration of technical mentoring. Djangonaut Space is an excellent on ramp to contributing, but obviously is just that. The question to me is what could the Steering Council do to replace themselves by nuturing and mentoring community members to grow the pool of possible candidates.<\/p>\n<p>Underlying all of these ideas is one of transparency and open communication, with the next council reporting back to the community with active work they have done in the capacity of technical leadership.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-203","title":"Steering Council Wishlist for 6.X","summary":"Defining Django for the next decade.","date_modified":"2024-10-18T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#steering-council"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-202","content_html":"<p>These posts tend to either come easily or are a stretch. Right now it&#x27;s 10pm and I want to go to bed, but this post has yet to be written, so here I am. When these posts come easily I can normally write early in the morning and get it done. Then there are day&#x27;s like today, when I don&#x27;t have a solid idea of what topic to write about. I have idea&#x27;s or follow up&#x27;s to previous posts (eg DSF Board elections, further responses to my forum posts) but they haven&#x27;t quite solidified in my mind to write a decent post about it, that I would be happy about it.<\/p>\n<p>I do have a list of potential topics, but none of them sufficiently grab me to write about, I will get through them one day, but they are for another day.<\/p>\n<p>I&#x27;ll finish today with a question. What do you want me to write about that I haven&#x27;t already? Please let me know :D<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-202","title":"Stuggling to write today","summary":"What to do when a topic or idea will not form in my mind","date_modified":"2024-10-17T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#writers_block","#meta"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-201","content_html":"<p>Recently I have been building out an admin using <a href=\"https:\/\/uibakery.io\/\">UI Bakery<\/a> on top of supabase to enable some onboarding without non technical users getting confused with the supabase UI. Honestly I would expect supabase to acquire a tool like this or build one into their product but that is beside the point for today.<\/p>\n<p>I have enjoyed the flexibility and speed of building out quick admin pages with a drag and drop UI building interface with a combination of low-code actions to perform the necessary updates to the database or call an API.<\/p>\n<p>You can probably guess what I am imagining? What if something like this was available for the Django Admin? Not to replace the existing tools, but to extend them to quickly build out pages visually within certain limits and to do this within a deployed environment and make queries using the ORM. I certainly would expect something like this to be a paid thing (be it a licenced package or product) to get match the level of polish that these services provide. The investment doesn&#x27;t come for free!<\/p>\n<p>Or perhaps something like these already exist within the ecosystem and I&#x27;m not aware of it? I know I don&#x27;t know every package related to the admin, so likely have missed something. While writing this I remembered that the Jet theme evolved into the <a href=\"\">Jet admin<\/a> which has <a href=\"https:\/\/docs.jetadmin.io\/user-guide\/integrations\/django-framework-package\">Django support<\/a>. Something to investigate for another day!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-201","title":"Drag and Drop UI builder idea for the Django Admin.","summary":"Super-charging the Django Admin... possibly","date_modified":"2024-10-16T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#admin","#package_idea"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-200","content_html":"<p>Django is well known for it&#x27;s for stabiltiy and it&#x27;s slow pace which can be annoying is a strength, especially given the stength of the third-party packages in the ecosystem. It&#x27;s also fairly well known that the community suggests that a third-party package is the first step in proposing a new feature for Django. There is a benefit to a third-party package, it&#x27;s faster to churn out releases as the design of the feature get&#x27;s fleshed, bugs can be fixed quicker and breaking changes can happen quicker as well. Essentially Django core is slow, process heavy and stable and for good reason.<\/p>\n<p>However, I do wonder (<a href=\"\/100-words\/day-184\">as I<\/a> <a href=\"\/100-words\/day-183\">written<\/a> <a href=\"\/100-words\/day-78\">before<\/a>), if there is a middle ground in the form of blessed packages (that can change). My latest idea &amp; experiment for this is allowing a list of approved (third-party) packages to be installed with in <code>django.contrib<\/code>. Technically it&#x27;s possible as I created this package (<a href=\"https:\/\/github.com\/nanorepublica\/django-contrib-test\">github<\/a>, <a href=\"https:\/\/pypi.org\/project\/django-test-contrib\/\">pypi<\/a>) which can be installed separately and looks like a core <code>contrib<\/code> package.<\/p>\n<p>I would not advocate for a free-for-all in this regard, I would suggest some checks that verify the list of contrib apps in <code>INSTALLED_APPS<\/code> against a hardcoded list (or possibly a setting?). This would mean that the community still verifies what is considered a <code>contrib<\/code> app.<\/p>\n<p>The other benefit to these third-party <code>contrib<\/code> apps is that a stable version could be included into core, then it&#x27;s down to the choice of an individual team\/developer if they want to pick up the latest versions by upgrading the third-party install. Perhaps we could also leverage virtual packages to include these extra contrib packages. Essentially it allows us to pick slow and steady as well as the latest &amp; greatest for a these packages.<\/p>\n<p>Finally one things that I would want to be clear is that I don&#x27;t want to put anymore maintenance burden onto core than is necessary to allow a migration path for new features\/batteries to get the wide adoption as a &#x27;first party&#x27; package?<\/p>\n<p>What do you think? Is this a crazy idea or do you think it has some legs? Please do checkout the package\/code above, I&#x27;m interested in what this idea could unlock!<\/p>\n<p>P.S. Woop for day 200!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-200","title":"Adding a new contrib package to Django","summary":"Can we have fast moving experiments in core?","date_modified":"2024-10-15T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#contrib"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-199","content_html":"<p>In my last post, I had some questions about the <code>contrib<\/code> namespace with in Django core. I also answered a few of them, today I&#x27;m going to continue answering the rest of them.<\/p>\n<p>The next group of questions were about whether any of the packages should move out or in of <code>contrib<\/code> or just remain in <code>contrib<\/code>. After writing my post on Friday, I had some reflections on what makes packages in <code>contrib<\/code> unique, first they are the only Django apps inside core, ie they need to be specified inside <code>INSTALLED_APPS<\/code>, some have migrations etc.. they also shouldn&#x27;t be essential to every Django project in existance. This second point is hard to pin down since Django doesn&#x27;t collect usage statistics of any kind, but a fair number of <code>contrib<\/code> packages are included by default.<\/p>\n<p>The second major aspect I considered was the dependencies both with in the <code>contrib<\/code> namespace and with <code>contrib<\/code> and the rest of the Django codebase. Using the <a href=\"https:\/\/pypi.org\/project\/impulse\/\">Impulse CLI<\/a> I mapped the the internal dependencies which can be seen below. This illustrates an interesting point, that some packages depend on each in different ways (admin &amp; auth) and also some packages which would be considered essential today (eg auth) depend on packages considered more optional (sites &amp; contenttypes). The final aspect of looking at this diagram is that perhaps the packages with no links could be moved (eg sessions or humanize) or those that depend on no other packages (staticfiles and messages). What is clear is that there isn&#x27;t an easy answer for every package within <code>contrib<\/code><\/p>\n<p><img src=\"\/contrib.png\" alt=\"Dependency graph of Django contrib package\"\/><\/p>\n<p>Looking at the dependencies with other parts of the Django codebase, there are two main dependencies. <code>staticfiles<\/code> depends on <code>django.test<\/code> and <code>django.templatetags<\/code> and <code>auth<\/code> depends on <code>django.test<\/code>. To me this elevates <code>staticfiles<\/code> to be a candidate for migrating into core fully as it&#x27;s has no dependencies within <code>contrib<\/code> and has &#x27;external&#x27; dependencies.<\/p>\n<p>However for most of the oher packages within <code>contrib<\/code> I think they need to stay where they are, or any moves need to considered along with there dependencies. The alternative is to perhaps breaks an app up so parts live in different packages within core?<\/p>\n<p>The above in part answers the question, that <code>contrib<\/code> still has a place in Django, but finally how do we as a community add a new package to contrib? Could <code>contrib<\/code> be a place to experiment with new batteries for Django? Is it a place for select &#x27;first-party&#x27; packages? Could it be a place for faster experimentation? Can we our cake and eat it? I&#x27;ll explore more about this tomorrow.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-199","title":"What is the future of `django.contrib`...?","summary":"Can we move the namespace forward?","date_modified":"2024-10-14T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#contrib"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-198","content_html":"<p>Today I was reading <a href=\"https:\/\/emma.has-a.blog\/articles\/django-wishlist-explained.html\">Emma&#x27;s post<\/a> from today about her wishlist for Django, which expands on some of items I listed since she did contribute to the forum post. Twice she mentioned adding to <code>contrib<\/code> with in Django and this prompted some questions...<\/p>\n<ol>\n<li>When was a package last added to the <code>contrib<\/code> package?<\/li>\n<li>When was a package last removed from the <code>contrib<\/code> package?<\/li>\n<li>What does it take to get a package added to <code>contrib<\/code>?<\/li>\n<li>What is the meaning of <code>contrib<\/code> with in Django?<\/li>\n<li>Which packages still belong in <code>contrib<\/code>?<\/li>\n<li>Which packages should be consider core and no longer in <code>contrib<\/code>?<\/li>\n<li>Which packages should be migrated out of <code>contrib<\/code> to a third-party package?<\/li>\n<li>Finally should <code>contrib<\/code> still exist today?<\/li>\n<\/ol>\n<p>I don&#x27;t have answers to all of these questions right now, but let&#x27;s attempt some of them over the next few posts.<\/p>\n<p>First up: &quot;What is the meaning of <code>contrib<\/code> with in Django?&quot; Well the docs state this <a href=\"https:\/\/docs.djangoproject.com\/en\/5.1\/ref\/contrib\/\">here<\/a>:<\/p>\n<blockquote>\n<p>Django aims to follow Python\u2019s <a href=\"https:\/\/docs.python.org\/3\/tutorial\/stdlib.html#tut-batteries-included\">\u201cbatteries included\u201d philosophy<\/a>. It ships with a variety of extra, optional tools that solve common web development problems.<\/p>\n<\/blockquote>\n<p>This paragraph was has been unchanged since it&#x27;s first inception 19 years ago on 22nd November 2005! Similarly the final section of that page about <a href=\"https:\/\/docs.djangoproject.com\/en\/5.1\/ref\/contrib\/#other-add-ons\">&quot;Other add-ons&quot;<\/a> has been unchanged.<\/p>\n<p>This last section feels very out of date since the last time a package was added to contrib was pre 1.0 (at least 15 years ago) and the 15 remaining packages have been static since 1.10 (when Django started being very stable). This really means the last section of contrib docs I referred to is very out of date, both in where to mention a new potential package but also it&#x27;s very unlikely to be included in <code>contrib<\/code> today.<\/p>\n<p>This does beg answers to the later questions and whether <code>contrib<\/code> is still valid today or what could be changed to add new batteries to Django via <code>contrib<\/code>?<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-198","title":"What to do with `django.contrib`...","summary":"Has the contrib package lost all meaning?","date_modified":"2024-10-11T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#contrib","#batteries"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-197","content_html":"<p>Yesterday I explored some packages that could be advertised as solutions to the missing batteries. Today it&#x27;s a quick cover of the rest of the list which broadly falls into a few categories: items that could potentially be merged into core in some fashion, items that are already been worked on or items that need further definition.<\/p>\n<p>The same disclaimer applies as of yesterday, these are my own opinions and will have some holes!<\/p>\n<p>Items already in progress:<\/p>\n<ul>\n<li>Listing all URLs<\/li>\n<li>Saw something about making emails sending more modern, can\u2019t wait!<\/li>\n<li>Saw something about making login\/logout more out-of-the-box, can\u2019t wait either!<\/li>\n<\/ul>\n<p>Possible items for to be considered for core (even if they start as a third-party package):<\/p>\n<ul>\n<li>JSON logging<\/li>\n<li>Organization of management commands<\/li>\n<li>Recommended app structure or enhanced startproject structure<\/li>\n<li>Auto-import of signals<\/li>\n<li>Migrations being valid for black<\/li>\n<\/ul>\n<p>Finally these final items likely need packages or I missed listing the package options for these batteries yesterday:<\/p>\n<ul>\n<li>.env file support<\/li>\n<li>per-environment settings files<\/li>\n<li>built-in history on fields<\/li>\n<\/ul>\n<p>What is clear to me is the imperative to continue to highlight third-party packages in the documentation which is to recognise them as a core part of the Django ecosystem that they are.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-197","title":"Creating canonical Django packages - Part 5","summary":"Other batteries could be added to core","date_modified":"2024-10-10T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#batteries","#contributions"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-196","content_html":"<p>Yesterday I summarized a forum post about missing batteries that the community feel is missing from Django. Today I&#x27;m going to pick out some existing packages that fulfil the missing batteries specified.<\/p>\n<p>Before I go any further a bit of a disclaimer on the packages I mention. They are packages I know about that meet my understanding of the batteries outlined or meet some of the requirements. I also haven&#x27;t used all of them, some are based on passing knowledge.<\/p>\n<p>So without further ado:<\/p>\n<ul>\n<li>Deployments &amp; Hosting\n<ul>\n<li>Export to Static - django-distill<\/li>\n<li>Static asset handling - whitenoise<\/li>\n<li>First time deployments - django-production<\/li>\n<li>Cloud object storage - django-storages<\/li>\n<li>simpler deploys - django-simple-deploy<\/li>\n<\/ul>\n<\/li>\n<li>Feature flags\n<ul>\n<li>django-waffle<\/li>\n<li>django-flags<\/li>\n<\/ul>\n<\/li>\n<li>APIs &amp; related tooling\n<ul>\n<li>Django Rest Framework<\/li>\n<li>Django Ninja<\/li>\n<\/ul>\n<\/li>\n<li>Advanced authorization\/Roles &amp; permissions\n<ul>\n<li>django-rules<\/li>\n<li>django-guardian<\/li>\n<\/ul>\n<\/li>\n<li>Advanced forms (presentation \/ custom fields \/etc)\n<ul>\n<li>I don&#x27;t know of any here!<\/li>\n<\/ul>\n<\/li>\n<li>Datatables (record lists with sort \/ search \/ filter \/ paginate)\n<ul>\n<li>django-tables2<\/li>\n<li>iommi<\/li>\n<\/ul>\n<\/li>\n<li>CSV import\/export\n<ul>\n<li>Django Import Export<\/li>\n<\/ul>\n<\/li>\n<li>Payments &amp; billing\n<ul>\n<li>dj-stripe is a starting point for this.<\/li>\n<\/ul>\n<\/li>\n<li>Background tasks\n<ul>\n<li>Django Tasks<\/li>\n<\/ul>\n<\/li>\n<li>Python types\n<ul>\n<li>django-types<\/li>\n<li>django-stubs<\/li>\n<\/ul>\n<\/li>\n<li>Template components\n<ul>\n<li>django-components<\/li>\n<li>django-cotton<\/li>\n<li>django-unicorn<\/li>\n<li>django-viewcomponent<\/li>\n<li>django-web-components<\/li>\n<li>slippers<\/li>\n<li>django-bird<\/li>\n<\/ul>\n<\/li>\n<li>Templates not failing silently\n<ul>\n<li>django-fastdev fixes this.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>Not all the batteries are listed above, because some of them should be in core or I&#x27;m not sure of which packages would fit the gap as I haven&#x27;t explored that space myself well enough.<\/p>\n<p>Finally there is an general expectation that packages should be free to access, and while admirable I don&#x27;t believe that the more advanced batteries should not be limited in this manner. Charging for access to some packages that provide complex functionailty should enable a package maintainer to sustainably support a package. There are examples of this in other communities (sidekiq, laravel nova).<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-196","title":"Creating canonical Django packages - Part 4","summary":"Matching missing batteries to existing packages","date_modified":"2024-10-09T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#batteries","#packages"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-195","content_html":"<p>After my last post of the subject on <a href=\"\/100-words\/day-184\">Day 184<\/a>, I opened up this <a href=\"https:\/\/forum.djangoproject.com\/t\/if-django-were-created-today-which-batteries-would-you-expect-to-be-included-in-core\/34934\/12\">forum post<\/a> asking the question:<\/p>\n<blockquote>\n<p>If Django were created today, which batteries would you expect to be included in core?<\/p>\n<\/blockquote>\n<p>It&#x27;s been a almost 3 weeks which is a reasonable amount of time with 12 replies so far and was mentioned in the Django News email. Below is a quick summary of the responses:<\/p>\n<ul>\n<li>Deployments &amp; Hosting\n<ul>\n<li>Export to Static<\/li>\n<li>Static asset handling<\/li>\n<li>A more \u201cmonolithic\u201d runserver command \/ docker image, that does all required startup tasks automatically and in the background (running migrations, collecting staticfiles, etc)<\/li>\n<li>First time deployments<\/li>\n<li>Cloud object storage<\/li>\n<li>simpler deploys<\/li>\n<\/ul>\n<\/li>\n<li>Feature flags<\/li>\n<li>APIs &amp; related tooling<\/li>\n<li>HTMX tooling\n<ul>\n<li>A blessed integration with frontend build processes<\/li>\n<\/ul>\n<\/li>\n<li>Documentation \/ Debug ToolING\n<ul>\n<li>Having official tooling\/recommendations for development like templates formatter, docker wrapper, editors extensions (vim &amp;vs code) and front-end task runner.<\/li>\n<li>more illustrative error messages (templates not found, db columns not found when migration is needed, etc) and links to the docs<\/li>\n<\/ul>\n<\/li>\n<li>Advanced authorization (eg: per record \/ per field \/ etc)\n<ul>\n<li>Role and permission framework<\/li>\n<\/ul>\n<\/li>\n<li>Advanced forms (presentation \/ custom fields \/etc)<\/li>\n<li>Datatables (record lists with sort \/ search \/ filter \/ paginate)<\/li>\n<li>CSV import\/export<\/li>\n<li>Payments &amp; billing<\/li>\n<li>Background tasks<\/li>\n<li>.env file support<\/li>\n<li>per-environment settings files<\/li>\n<li>Listing all URLs<\/li>\n<li>Python types<\/li>\n<li>JSON logging<\/li>\n<li>Organization of management commands<\/li>\n<li>Recommended app structure or enhanced startproject structure<\/li>\n<li>Auto-import of signals<\/li>\n<li>Migrations being valid for black<\/li>\n<li>built-in history on fields!<\/li>\n<li>Saw something about making emails sending more modern, can\u2019t wait!<\/li>\n<li>Saw something about making login\/logout more out-of-the-box, can\u2019t wait either!<\/li>\n<li>Template components<\/li>\n<li>Templates not failing silently<\/li>\n<\/ul>\n<p>That&#x27;s a lot and I&#x27;m sure there is more. However what strikes me the most is that a lot of these things are already in progress or have a package to meet the need. What&#x27;s missing is either plugging the low hanging fruit by pulling fixes out of packages into core or by &#x27;blessing&#x27; a package and a follow up with investment by the community into that package.<\/p>\n<p>I have some opinions about what that investment looks like and will continue to write about this. The next step is match some of these missing batteries up to some existing packages. Then working out where they fit into the docs or perhaps something more dramatic?<\/p>\n<p>Finally I enjoyed this comment, since the post was inspired by me browsing the Laravel site! Go look at the ecosystem menu on the Laravel site.<\/p>\n<blockquote>\n<p>In general, Django should learn more from Laravel and maybe Rails regarding DX<\/p>\n<\/blockquote>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-195","title":"Creating canonical Django packages - Part 3","summary":"Which batteries do the community think are missing?","date_modified":"2024-10-08T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#batteries","#packages"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-194","content_html":"<p>Most of my Django career has been brownfield projects, I have walked into code written by others and they are often no longer around, so I am left to make the most of the code presented as is.<\/p>\n<p>My first step is to sort out a local docker compose setup, either using something present in the repo or creating one. This includes using minio for S3 like storage if required, mkcert for SSL &amp; HTTPS locally.<\/p>\n<p>Next I take time to manually map out the dependencies between Django apps (if any exist). I view Django apps as logical sections of the project and so there ought to be strict dependencies between them. I also include database relationships in this map making. This is a combination of tools (pyreverse, graph_models, impluse-cli) and some manual work to get it all together.<\/p>\n<p>Typically this highlights some work to remove a circular dependency or some logical issue. I can then create tickets for these to be addressed in order so eventually the project is a single acyclical directed graph. I find the graph easy to modify in a dot file which allows me to comment out edges to see what the effect would be on the project, essentially experimenting with the logical project layout. I will then ticket up what needs to happen to make the dependencies work as expected.<\/p>\n<p>Finally if there is a single app, then this would be done on a &#x27;smaller&#x27; scale with python modules and not apps or I would be inspecting models to understand the structure of the project and would start by moving relevant views, forms, templates &amp; urls into different apps, before finally moving models around once the app structure has worked itself out.<\/p>\n<p>There is more write, but that&#x27;s enough of a summary for today.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-194","title":"Refactoring a Django project","summary":"My approach when walking into an existing Django project","date_modified":"2024-10-07T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#apps","#refactoring"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-193","content_html":"<p>Today I had my monthly meeting with myself (drinking a biscoff hot chocolate) and reflecting that I am hitting the limits of my time and how much I want to achieve vs how much I am actually achieving. While I&#x27;m not looking to scale my current business due to the co-founder role I have accepted, I will be going beyond a team of one in that role so the below still applies.<\/p>\n<p>First up is that I do get distracted throughout the day, be it emails, youtube or other distractions, but they will always exist. The real crux is that if I want to sub-contract or outsource my work to others, I need get better at communicating the precise knowledge in my head about the system and how it relates to the task at hand and clearly write it up so the other developer can effectively work on it. I accept this is a skill that can learned and honed when working in a team and if I were in a team that would happen more naturally.<\/p>\n<p>However I wonder if I can practice this skill when working solo? What incentives could I put in place to ensure I write up the technical solution space before working on it. Could perhaps a video screenshare or audio recording be more effective for the other person?<\/p>\n<p>As per usual I have more questions right now than answers for myself. I think the real crux is an incentive stucture that works. Right now it would take me longer to record the how to do a ticket compared with just solving a ticket and pushing the code out.<\/p>\n<p>I think perhaps I need to start recording myself when I am working. This ought to make me more intentional with my work and hopefully record knowledge to pass onto others.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-193","title":"Scaling myself as a freelancer or solo developer","summary":"Do the work or record how to do the work?","image":"https:\/\/softwarecrafts.co.uk\/photos\/day-193.jpg","date_modified":"2024-10-04T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#freelancing","#processes","#scaling","#tickets"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-192","content_html":"<p>Earlier this week Justin Jackson shared a link to this short podcast <a href=\"https:\/\/episodes.fm\/1480884324\/episode\/MzJkOWViMmUtZmUyNC00ZmIzLTk4YjctMmI2MWFlNzk3ZWIy\">episode<\/a> from Caleb Porzio about how he records his episode and it immediately resonated with me, he uses <a href=\"https:\/\/rogueamoeba.com\/audiohijack\/\">Audio Hijack<\/a> to record the episode then uploads it straight to Transistor.fm his podcast platform of choice.<\/p>\n<p>So I downloaded Audio Hijack myself and today recorded a short episode, however my platform of choice is going to be <a href=\"https:\/\/ripple.fm\/\">Ripple.fm<\/a>, a recent creation from Brian Casel which has a twist to allow private podcasts with the aim of bringing listeners closer to the podcast creator to allow for discussions on episodes etc.<\/p>\n<p>The first episode is live, I&#x27;m going to keep them short (sub 10 minutes) and likely use it as an audio journal of sorts, where I talk about what I&#x27;m doing that&#x27;s in progress. I like the name as it has potential for other things, but I&#x27;m sticking with the podcast for now. I&#x27;m not sure what cadence it will be on, whether daily, weekly or when progress is made, we shall see!<\/p>\n<p>Have <a href=\"\/in-progress\">a listen<\/a> if your interested!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-192","title":"A new project: In progress","summary":"Most things in life are never finished","date_modified":"2024-10-03T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#new_project","#podcast"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-191","content_html":"<p>I have <a href=\"\/100-words\/day-165\">previously<\/a> <a href=\"\/100-words\/day-174\">mentioned<\/a> that I am joining a new startup and today I have finally got access to my new email (Thanks for nothing <a href=\"\/100-words\/day-182\">GoDaddy!<\/a>), which means I can start signing up for the tools I would like us to use for this company.<\/p>\n<p>My cofounder and I both would like a calm company, one that is measured in it&#x27;s pace. This means we make progress but it&#x27;s smooth rather than frantic. As such I want to use communication tools that support this way of working. Also to follow the framework I mentioned last week of external, internal, bridging the gap, getting work done and casual communication. This will limit the tools we choose and they will be intentional choices.<\/p>\n<p>So far we have the following:<\/p>\n<ol>\n<li>External communication: Email - it has to factor somewhere and this is what most of the world is familiar with.<\/li>\n<li>Internal communication: <a href=\"https:\/\/twist.com\">Twist<\/a> (from Doist) - for slower more intentional communication<\/li>\n<li>Bridging the gap: Undecided as yet, although emails can be forward to Twist directly.<\/li>\n<li>Where work gets done: <a href=\"https:\/\/linear.app\">Linear<\/a> - I have heard lots of good things from other small teams and this feels like a perfect time to try it out!<\/li>\n<li>Casual communication: Twist again for now, or it&#x27;s going to be a phone call.<\/li>\n<\/ol>\n<p>Do you know any tools which prompt calm, considered companies? I&#x27;m open to evaluating new tools that fill a gap or may replace one of the tools above.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-191","title":"Picking the right tools for the job","summary":"Picking tools influences the culture you want to build","date_modified":"2024-10-02T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#tools","#communication","#startups","#culture"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-190","content_html":"<p>As I mentioned yesterday being a moderator is to be a culture creator &amp; enforcer. Luckily for us in the moderation team the wider Django community has an established <a href=\"https:\/\/www.djangoproject.com\/conduct\/\">Code of Conduct<\/a> which outlines a base line of what is considered acceptable. This code informs the proactive actions we choose to enforce.<\/p>\n<p>An example of this is to encourage people not to use the word &#x27;guys&#x27; in their vocabulary when addressing the room. This is a small change which promotes inclusivity in a industry dominated by men and the minorities are unlikely to speak up against this.<\/p>\n<p>Otherwise it&#x27;s mostly housekeeping such as keeping the channels on topic (a common one is a new person sharing a link in a help channel where show-and-tell is more appropriate.) or cleaning up posts as mentioned yesterday.<\/p>\n<p>Finally the most important thing to me is to help people out and interact on the server. This is leading by example and creating the culture by demonstrating it with our words through what we type and how we interact with others asking questions.<\/p>\n<p>As I mentioned earlier there are times we get it wrong and there is work to create some uniformity in how we react to posts as well as sharing the load. Moderating can be tough, emotionally draining work, it means setting aside your personal opinions when a conversation gets heated and just looking at the facts of the situation.<\/p>\n<p>I do hope we get a Community Working Group started which can provide some governance and support around our online spaces that give people the opportunity to take a break when they need it.<\/p>\n<p>PS a final note on this is to ask the question: &quot;What Would Tim (Shilling) Do?&quot; who always answered with grace and respect during his time as a mod! #appreciation<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-190","title":"Moderating a Discord Server - 2","summary":"Cultivating a culture in line with the wider community","date_modified":"2024-10-01T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#discord","#community","#moderation"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-189","content_html":"<p>Discord is a community platform, a digital <a href=\"https:\/\/en.wikipedia.org\/wiki\/Third_place\">third place<\/a> where people meet with a common interest. As such, I see my role as a moderator as one as a culture creator by keeping things neutral yet accessible to all. It&#x27;s a balancing act and one that we will not always get right. To be human is to make mistakes, but then owning up to them.<\/p>\n<p>With that introduction out of the way, here&#x27;s how we deal with things in the Django Discord. The majority of the actions we take involve spam of various kinds:<\/p>\n<ul>\n<li>Hacked or compromised accounts<\/li>\n<li>Job solicitations or questionable job posts<\/li>\n<li>Other spam that doesn&#x27;t fit the community &amp; culture of Django.<\/li>\n<\/ul>\n<p>First we typically reply to the message with the offence or use a reaction to trigger our MEE6 bot to respond to the message. Then we delete the message.<\/p>\n<p>For job solicitation posts we typically warn them multiple times before banning them, maybe with a timeout or two. Hacked accounts typically get banned straight away since they spam all possible channels and banning is the easiest way to clear those messages within Discord. Other spam messages will likely get a warning and a timeout for the first offence, then the next offence will result in a ban.<\/p>\n<p>This could definitely be more formal as each moderator might follow a slightly different route, there is also communicating about users between the moderation team.<\/p>\n<p>More notes to follow about other aspect of how we moderate as these will get pulled into a more formal document for the community to use and contribute to across the platforms.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-189","title":"Moderating a Discord Server","summary":"Dealing with spam messages and accounts","date_modified":"2024-09-30T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#discord","#community","#moderation"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-188","content_html":"<p>There is a variety of questions that is asked often enough by newcomers to Django which is along the lines that they have completed the tutorial, then they feel a bit lost at what to do next. This also often crops up within with in the specific realm of create web APIs as well.<\/p>\n<p>The answer often given is to crack on with project and then ask specific questions\/problems as they come up. I find this answer truthful but lacking as it assumes the question asker is familiar with how &quot;the web&quot; (HTTP, client\/server model, data modelling, Content types and whole lot of other theory) works and that they haven&#x27;t learned some falsehoods or bad practices along the way.<\/p>\n<p>Essentially what they are lacking is a direction or roadmap after the tutorial. The documentation is dense, <a href=\"https:\/\/docs.djangoproject.com\/en\/5.1\/intro\/tutorial08\/#what-s-next\">post the tutorial<\/a>, it suggests reusable apps as the next thing that looks like a tutorial, or it&#x27;s a <a href=\"https:\/\/docs.djangoproject.com\/en\/5.1\/intro\/whatsnext\/\">page<\/a> which details how to read the docs, but gives no hint on what topics would be appropriate to read next. Just today a newcomer mentioned the docs being too dense and honestly I wouldn&#x27;t want to change that, the depth and density is a reassurance to myself knowing that there is more to learn a decade in.<\/p>\n<p>What I am suggesting is that we create a direction of travel for newcomers. The website <a href=\"https:\/\/roadmap.sh\">roadmap.sh<\/a> would be an excellent tool to create this roadmap or at least create a draft to then be formatted into a new documentation page. Perhaps doing a more Django specific version of <a href=\"https:\/\/roadmap.sh\/backend\">this one<\/a>?<\/p>\n<p>Off the top of my head we probably need to be pointing newcomers to the auth docs, the admin docs, ORM pages and also sites like ccbv and djangopackages and other recommended third party packages.<\/p>\n<p>Do you agree? Should this be a new docs page?<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-188","title":"A Django roadmap for newcomers","summary":"Can we create a community roadmap?","date_modified":"2024-09-27T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#roadmaps","#learning_pathways","#documentation"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-187","content_html":"<p>This week I have been adding exports to a client Django admin for GDPR reasons. While doing this task it triggered a question or an idea, which was along the lines of:<\/p>\n<blockquote>\n<p>I wonder if it&#x27;s possible to filtered the whole admin by this single user? Are global filters a thing?<\/p>\n<\/blockquote>\n<p>Having pondered it for a couple of days, this is probably where custom views would be more efficient way to do it since navigating the different database designs generically in a performantly is beyond my pay grade (or at least feels like it!)<\/p>\n<p>Putting custom views aside and considering if it were to be done in the admin. I think having a filter or two be located in the header or opposite the breadcrumbs for the app and model. My thinking is that you would need to specify something globally, either register a global filter with the site object or subclass the admin site class. Next would be specifying how individual admins interact with that filter. For example a global date filter might be useful, but different models may want to intepret which date field(s) gets applied to the global filter.<\/p>\n<p>Below is some possible pseudo-code of how the interface could look:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\">\n<span class=\"token decorator annotation punctuation\">@admin<span class=\"token punctuation\">.<\/span>register_filter<\/span><span class=\"token punctuation\">(<\/span>User<span class=\"token punctuation\">)<\/span>\n<span class=\"token keyword\">class<\/span> <span class=\"token class-name\">UserFilter<\/span><span class=\"token punctuation\">(<\/span>admin<span class=\"token punctuation\">.<\/span>GlobalModelFilter<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    filter_name <span class=\"token operator\">=<\/span> <span class=\"token string\">&#x27;my_user_filter&#x27;<\/span>\n\n\n<span class=\"token decorator annotation punctuation\">@admin<span class=\"token punctuation\">.<\/span>register<\/span><span class=\"token punctuation\">(<\/span>MyModel<span class=\"token punctuation\">)<\/span>\n<span class=\"token keyword\">class<\/span> <span class=\"token class-name\">MyModel<\/span><span class=\"token punctuation\">(<\/span>admin<span class=\"token punctuation\">.<\/span>ModelAdmin<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    global_filter_fields <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">{<\/span>\n        <span class=\"token string\">&#x27;my_user_filter&#x27;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token punctuation\">[<\/span><span class=\"token string\">&#x27;user&#x27;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token string\">&#x27;my_other_model__user&#x27;<\/span><span class=\"token punctuation\">]<\/span>\n    <span class=\"token punctuation\">}<\/span>\n<\/code><\/pre>\n<p>Here the intention is to register a global filter, then the model admin is specifying the ORM filter LHS kwargs to use when interacting with that model admin. Hopefully that makes sense?<\/p>\n<p>What do you think to this? Would this be a good addition to the admin? Obviously this would be best tested as a third-party package first.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-187","title":"Django Admin global filters","summary":"A possible idea for the admin","date_modified":"2024-09-26T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#package_idea"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-186","content_html":"<p>After yesterday&#x27;s post <a href=\"https:\/\/indiehackers.social\/@CodenameTim@fosstodon.org\/113192040022225150\">Tim Shilling<\/a> correctly pointed out that I had missed two major online platforms that the community uses, Mastodon &amp; blog posts.<\/p>\n<p>Both of these platforms are in casual, informal category as well as it fitting into external communication category. They are normally the starting place for new ideas or longer thought opinion pieces as well as sharing event photos from conferences etc! (search for #djangoconus on Mastodon to see what I mean!). These places are important to allow for opinionated expressions that aren&#x27;t limited by the shared platforms and allow for nutruring of new ideas.<\/p>\n<p>Finally, a bonus platform are in person events where large (DjangoCon&#x27;s) or small (local meetups). These are places where longer discussions happen and friendships within the community forms. Having only been to a single conference I really got a sense of the heart of the community of Django, which was amazing. If you are part of the community I can recommend getting along to one of the conferences or a meetup whenever you can!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-186","title":"Part 2 of Django community platforms","summary":"A follow up to yesterday's post","date_modified":"2024-09-25T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#community"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-185","content_html":"<p>The Django community uses a number of platforms to community about the work that needs to be done. To frame this post, I&#x27;m going to borrow a concept from <a href=\"https:\/\/www.arimeisel.com\/\">Ari Meisel<\/a>, where he states that an organisation only needs 4-5 communication tools, each serving a specific need. Here are the specific needs:<\/p>\n<ol>\n<li>External communication<\/li>\n<li>Internal communication<\/li>\n<li>Bridging the gap between Internal &amp; External communication tools<\/li>\n<li>Where work gets done.<\/li>\n<li>Casual communication<\/li>\n<\/ol>\n<p>As we are a community with fuzzy edges, I&#x27;m not suggesting we adhere strictly to these groupings but use it as a lens to group the tools that are used. Currently I am aware of the following tools that are used across the community:<\/p>\n<ol>\n<li>Trac<\/li>\n<li>Forum<\/li>\n<li>GitHub<\/li>\n<li>Discord<\/li>\n<li>Slack<\/li>\n<li>Mailing lists<\/li>\n<li>IRC<\/li>\n<li>Email<\/li>\n<\/ol>\n<p>First I would propose that the Mailing lists are deprecated in favour of the forum and I believe Discord has mostly replaced IRC but I see no harm in that staying around. This leaves us with six tools to categorise.<\/p>\n<p>Trac is a place where work gets done, along Github. The Forum is mostly internal communication along with Discord. Discord also covers more casual communication. The various Slack workspaces (DSF &amp; Conferences) are for internal communication. Email likely covers all of the areas. Essentially we have a fair amount of overlap for internal communication!<\/p>\n<p>One area I would like to focus on in particular is the <a href=\"https:\/\/github.com\/django\/deps\/\">DEPs<\/a> repository &amp; <a href=\"https:\/\/github.com\/django\/dsf-working-groups\">DSF Working Groups<\/a> repository. Personally I feel like these two gems are &#x27;hidden away&#x27; in Github since a conversation may start there, either in an issue or PR, but not get a lot of eyeballs since most communication happens either in the forum or Discord. Or a conversation may start in the forum or Discord and then appear to stop in it&#x27;s tracks as someone opens up an issue or PR on these repos. Essentially these repos seem to mix internal commuication and getting work done, but the topics feel more appropriate to me to happen on the forum?<\/p>\n<p>Therefore I would like to propose two changes:<\/p>\n<ol>\n<li>Pin both of these repos in the Django Github organisation<\/li>\n<li>Provide some level of deeper integration between these Github repos and the Forum, this could start as a one way notification or possibly a two way sync of comments?<\/li>\n<\/ol>\n<p>What do you think? Do any of these proposals make sense to you? Should I propose them more formally?<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-185","title":"A quick guide to platforms the Django Community uses","summary":"Where do the community talk and push things forward?","date_modified":"2024-09-24T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#community"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-184","content_html":"<p>I have been pondering more about my topic from Friday, especially as an email from Caleb Porzio announcing that FluxUI is now live dropped into my inbox this afternoon. To quote the lead paragraph<\/p>\n<blockquote>\n<p>The official Livewire component library.<\/p>\n<\/blockquote>\n<p>While FluxUI is one step removed from Laravel itself (though Livewire), Livewire is hosted on a subdomain of Laravel.<\/p>\n<p>To me this begs the question (which is another spin on Friday&#x27;s post). What is the definition of something being &quot;official&quot; in the Django ecosystem?<\/p>\n<p>Some probably mean it&#x27;s a feature being in core, others probably think it&#x27;s being hosted by the Django Github organisation. Finally there is something just being recommended through out the community which is fuzzy at best!<\/p>\n<p>I think clarifying what makes something &#x27;official&#x27; and then what support these packages get (from the DSF or a working group maybe?), this could be some budget, promotion on djangoproject.com in the appropriate place and a subdomain? I guess what I am proposing is something of a package that we as a community could take to the maintainers of the relevant packages to help them keep maintaining the packages they maintain. Something of a marketing bundle or the beginnings of one. Thinking about it, Ithink this would be a perfect thing for the draft packaging working group to consider.<\/p>\n<p>Ok, that&#x27;s enough for today. Tomorrow some more Django thoughts coming your way!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-184","title":"Creating canonical Django packages - Part 2","summary":"(Re)defining what it means to be 'official' in the Django ecosystem","date_modified":"2024-09-23T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#packages","#maintainers"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-183","content_html":"<p>Yesterday there was a fairly lively discussion on Mastodon started by @adamghill prompted by talks from LaraconUS. I jumped in with a few opinions, of my own, noting what Laravel as a framework does possibly differently and possibly better than Django.<\/p>\n<p>Laravel takes a neat trick of marketing features of the framework (and certain third party solutions) as products in their own right. This is done by giving each feature a distinctive name and pride of place in the top navigation under &#x27;Ecosystem&#x27;. Some have a well designed landing page to call home and a reasonable domain name, others are simply a link to the docs.<\/p>\n<p>Each of these packages\/features\/products are a battery in building a modern day web application, meeting a fairly common pain point that exists for pretty much every developer and also providing a guide for what we need to include in a project at some point.<\/p>\n<p>What can we as the Django Community learn from this? There are a few points that come to mind:<\/p>\n<ol>\n<li>Know the audience we are targeting with the home page of djangoproject.com and related pages<\/li>\n<li>Recognise which batteries we are missing from core or in general. To be 100% clear I am not saying we need to add these batteries to core.<\/li>\n<li>Recognise when a package has &#x27;won&#x27; for the moment.<\/li>\n<\/ol>\n<p>Where does this leave us with creating canoncial packages for Django? To me, it is about bringing a sense of cohesion to the ecosystem, allowing newcomers to navigate what&#x27;s available to get something shipped. This doesn&#x27;t mean there cannot be overlap (Laravel has this) or changes over time as community opinion changes. What it does mean is us as a community recognising that we get to a consensus on what to recommend in terms of packages that complement core and give them the resources (eg a sub domain) and position they deserve.<\/p>\n<p>However the next step to me is answering point 2 above, we need to identify which batteries are missing, then identify which packages fit the need. (Forum post incoming!)<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-183","title":"Creating canonical Django packages","summary":"Not being in Django core doesn't mean it hasn't 'won'","date_modified":"2024-09-20T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#missing_batteries","#third-party_packages"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-182","content_html":"<p>This morning I have been trying to complete the setup of a new email that has been created for me for a new project I am starting. The email account is Microsoft 365 hosted through GoDaddy... this is where it all goes wrong.<\/p>\n<p>I hit a bug on the webpage where I need to provide my new password. Passwords are entered fine, but it is also requiring a recovery email, except I am hitting the classic Javascript error of not being able to type anything and the Javascript console is throwing <code>TypeError: m is not a function<\/code>. Clearly something is broken and I try hacking my way around it as I notice it&#x27;s a React page to no success.<\/p>\n<p>My next step, as a reasonable internet citizen\/developer, is to report the bug so it can be fixed and I can move on with my day, except that is met with even more failure. I start chatting to GoDaddy support who cannot get beyond the requirement to verify my GoDaddy account details (I don&#x27;t have one), seem to ignore what I am actually typing an the images of the error I shared and finally refuse to pass me to their manager who may actually know what to do. Currently I&#x27;m left hoping that the engineering team at GoDaddy have decent error reporting?<\/p>\n<p>I saved the chat transcript, as an example to myself to never let customer service get like this for any company I build. Customer Service first and foremost needs to be people centric compared with process centric. When processes exist they need to empower the people doing the work to create meaningful relationships and in this case serve the customer. It&#x27;s very similar to how to talk with children, first acknowledge the problem, then move onto possible solutions.<\/p>\n<p>Finally I find it slighly infuriating that there is not obvious place on larger sites these days to report bugs like this. Every website has bugs, no website is perfect every hour of every day of every year, allowing users to report bugs is a quick win to me.<\/p>\n<p>PS I just found GoDaddy&#x27;s OSS email address so I&#x27;m pinging them to see if they can get it to the right people... fingers crossed!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-182","title":"How not to do customer service","summary":"Failing to report a bug at the first hurdle","date_modified":"2024-09-19T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#godaddy","#customer_service","#wrong_answers_only"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-181","content_html":"<p>Django&#x27;s migration system to me is world class, I wouldn&#x27;t really want to manage a database any other way when I have the choice. The guard rails Django provides are sensible and logical, however I have been doing Django for 12 years so I am biased!<\/p>\n<p>However newcomers to Django often seem to struggle with the migration system and end up having to reset their database from scratch and\/or deleting their migration files. With that said I&#x27;m going to walk through interacting with the Django migration system.<\/p>\n<p>Before we start, the first <em>golden rule<\/em> is <strong>not<\/strong> to interact with the database outside of Django, this is a common mistake and will often end in tears and wasted time. The second golden rule is to not to delete your migration files, unless you 100% know they haven&#x27;t been applied to the database.<\/p>\n<p>The first command is <code>makemigrations<\/code>. This command should only ever be used in development. It creates the migration files by examining the difference between your models.py and the current state of the database and then generates the appropriate commands in a new migration file. Migration files need to be commited to your source control of choice, they the instructions to any new environment of how to setup the project.<\/p>\n<p>The second command is <code>migrate<\/code>. This command can and should be run in any environment, normally it is run during development and on deployment to other environments. This command will apply any unapplied migrations to the database. This is achieved by comparing the migration files that exist in the codebase to the entries in the <code>django_migration<\/code> table (Again there is no need to alter this mannually in the normal running of a project). It is worth noting that migrate can rollback migrations as well as apply them. This is useful to fix issues as well as keep a clean migration history during development.<\/p>\n<p>Finally there is <code>showmigrations<\/code> this will print out a list of all migrations and if they have been applied or not. These are the commands that you will be using on a daily &amp; weekly basis.<\/p>\n<p>Other commands related to migrations are <code>squashmigrations<\/code>, <code>sqlmigrate<\/code> and <code>optimizemigration<\/code>, I have either used these sparingly or never used them. The first three commands are the ones to understand and master.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-181","title":"Django Migration Commands in a 100 words","date_modified":"2024-09-18T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#migrations","#management_commands"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-180","content_html":"<p>Some personal news today. We are having a air source heat pump installed this week so our house feels upside down with the lovely guys from <a href=\"https:\/\/octopus.energy\/get-a-heat-pump\/\">Octopus Energy<\/a> installing all the bits so that it will hopefully all be functional by the end of the week! This was helped by the UK Government offering \u00a37.5K towards the installation of it which meant it was an affordable investment.<\/p>\n<p>I&#x27;m also looking forward to getting onto Cosy Octopus which is their tariff designed for heat pumps, this will mean cheap electricity at certain points of the day. The other investment to offset our increased electricity bill is an investment into a co-op from <a href=\"https:\/\/rippleenergy.com\/\">Ripple Energy<\/a> which means an investment in green power generation and those savings past directly to our bill which currently looks to be may 25% to 33%!<\/p>\n<p>What&#x27;s the point of me telling you this? Well, I consider the long term implications of a decision, even if it means some short term pain. This applies when I&#x27;m designing or building a technical solution for your business.<\/p>\n<p>Some short term pain for long term gain.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-180","title":"Heat pumps and killing gas in our home","summary":"Green investments at a personal level","date_modified":"2024-09-17T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#green_energy","#octopusenergy","#no_gas"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-179","content_html":"<p>I was catching up on my podcast backlog and listening to end of <a href=\"https:\/\/changelog.com\/podcast\/570\">this episode<\/a> of The Changelog, specifically <a href=\"https:\/\/changelog.com\/podcast\/570#transcript-524\">this sentence<\/a> from Jerod. To quote he says this:<\/p>\n<blockquote>\n<p>It seems like unless you have a known vulnerability, staying one minor release behind is actually a best practice\u2026<\/p>\n<\/blockquote>\n<p>At that point my thought was how do you specify in a <code>pyproject.toml<\/code> or <code>package.json<\/code> something like that? As far as I know, when specifying a version in one of those files, there is nothing to do minus 1 on a minor release or specify some relative package specified (eg <code>latest - 1<\/code> or <code>3.X - 1.*<\/code>). I will fully accept that I personally am not an expert on all the possible syntaxes for specifying a package!<\/p>\n<p>I think the only thing I have seen come close to this is the <code>browsersList<\/code> key in a <code>package.json<\/code>, where there is a broader specification of whats acceptable. Here is an example of it, and it&#x27;s more verbose, but easier to read for sure.<\/p>\n<pre class=\"language-json\"><code class=\"language-json\"><span class=\"token property\">&quot;browserslist&quot;<\/span><span class=\"token operator\">:<\/span> <span class=\"token punctuation\">{<\/span>\n  <span class=\"token property\">&quot;production&quot;<\/span><span class=\"token operator\">:<\/span> <span class=\"token punctuation\">[<\/span>\n    <span class=\"token string\">&quot;&gt;0.2%&quot;<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token string\">&quot;not dead&quot;<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token string\">&quot;not op_mini all&quot;<\/span>\n  <span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">,<\/span>\n  <span class=\"token property\">&quot;development&quot;<\/span><span class=\"token operator\">:<\/span> <span class=\"token punctuation\">[<\/span>\n    <span class=\"token string\">&quot;last 1 chrome version&quot;<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token string\">&quot;last 1 firefox version&quot;<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token string\">&quot;last 1 safari version&quot;<\/span>\n  <span class=\"token punctuation\">]<\/span>\n<span class=\"token punctuation\">}<\/span>\n<\/code><\/pre>\n<p>I wonder if there is scope for relative specifications of dependencies or even more human readable specifies (eg <code>lts incl security patches<\/code>). I&#x27;m interested to hear your thoughts! Drop me a line if you know of something like this.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-179","title":"Relative versioning for packages","summary":"A new way to thing about dependency versioning?","date_modified":"2024-09-16T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#packages","#versioning"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-178","content_html":"<p>After many years of working remote, I try to get out to a local cafe once or twice a week. It&#x27;s mostly for the change of scene and to interact with humans other than my wife and two sons. Over time I have slowly built up my perfect portable office which means I can be packed up in a matter of moments and setup the other end. Below is my current setup.<\/p>\n<p>First up is Macbook Pro 14 inch, this is the essential whether working at home or a cafe. This is paired with a <a href=\"https:\/\/www.moft.us\/products\/moft-stand-adhesive?variant=39506278645847\">Moft Stand<\/a> and a case for the Macbook and a small bag for cables. My cables bag includes my macbook power cable, a USB A to USB C and a USB C to USB C, finally I recently discovered this slimline usb charged from <a href=\"https:\/\/alpkit.com\/products\/flat-stanley\">Alpkit<\/a>. To round of my power needs this <a href=\"https:\/\/www.apple.com\/uk\/shop\/product\/HQ352ZM\/A\/mophie-powerstation-pro-xl\">mophie powerstation pro XL<\/a> which is the largest they do, this means I can charge both my phone and laptop when I am out and about if a power socket isn&#x27;t available.<\/p>\n<p>When deep work calls, I have the <a href=\"https:\/\/www.amazon.co.uk\/Bose-QuietComfort-Wireless-Headphones-Cancelling-Black\/dp\/B0756CYWWD?th=1\">Bose QuietComfort QC35<\/a> headphones and currently prefer a Moleskin notebook to keep track of my top priorities (nothing beats crossing something out!). I also occasionally have my <a href=\"https:\/\/www.amazon.co.uk\/dp\/B0B7XD7R43?ref=ppx_yo2ov_dt_b_fed_asin_title\">DJi gimbal<\/a> for my phone where I would like to start capturing some b-roll for videos that I would like to start producing.<\/p>\n<p>This is all packed into my <a href=\"https:\/\/www.tailfin.cc\/product\/pannier-fork-bags\/rear-pannier\/ultra-durable-pannier-bag\/?v=79cba1185463\">tailfin pannier bag<\/a> which fits to the back of my bike.<\/p>\n<p>Finally I would like to add a second monitor, I have my eye on this <a href=\"https:\/\/uk.espres.so\/shop?display=v2&amp;size=13\">expresso monitor<\/a> which is super slim and light to enable the similar productivty of multiple screens on the go.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-178","title":"My Portable Office","summary":"My personl remote work setup","date_modified":"2024-09-13T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#remote_working","#portable_office","#equipment","#freelancing"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-177","content_html":"<p>Story time! When I was about a year into my first job at Ocado out of Uni, I was in the Netnix team (Networks &amp; *nix). We needed to hire a few new sysadmin people and decided it was time to update the technical interview to be a bit more modern in that we wanted to see how they would actually solve some simulated problems and also do a bit of scripting as well.<\/p>\n<p>It was quite an involved test, we setup two laptops (one client, one server) and the test was a python flask app running on the web server with realtime feedback of the answers. Half of the test involved fixing bits of the machines and getting information from server, the other half was doing some scripting on the log files and we could watch the participant via VNC without being in the room watching them.<\/p>\n<p>All that to say we expected them to use the Internet to search for answers, it was very much the against the classic interview of having to know everything and acknowledged that you do research problems and tools in real life.<\/p>\n<p>Recently, as I may need to start hiring again in the near future, I am considering the same thing in regard to AI. Tools, including AI, used correctly are fine in my books, but how do we realistically incorporate there usage into the technical portion of the hiring process.<\/p>\n<p>Currently I have a couple of ideas:<\/p>\n<ol>\n<li>Have an AI generate some code with bugs in it for the candidate to fix (this isn&#x27;t quite right, but a neat idea)<\/li>\n<li>Create a custom GPT which is limited in scope for the test (this is too easy to get around)<\/li>\n<li>Just let candidates use whatever they like, but like the original story, observe how they use the AI, if they do at all?<\/li>\n<\/ol>\n<p>Or something else entirely? I would love to hear your thoughts and ideas!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-177","title":"Technical tests in the age of AI","summary":"How do we allow for AI usage when evaluating candidates?","date_modified":"2024-09-12T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#ai","#hiring","#technical_tests","#llms"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-176","content_html":"<p>On <a href=\"\/100-words\/day-149\">day 149<\/a> I created a small <a href=\"https:\/\/github.com\/nanorepublica\/django_groups_poc\">prototype<\/a> which used the Group model to represent the <code>is_staff<\/code> and <code>is_superuser<\/code> flags. Today, we&#x27;re adding in the final piece of this experiment\/prototype, which is add have two custom permissions on the User model and use them to control the flags.<\/p>\n<p>First up we need to add the permissions to the User model through the <code>Meta<\/code> class (and then run migrations):<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token keyword\">class<\/span> <span class=\"token class-name\">User<\/span><span class=\"token punctuation\">(<\/span>AbstractBaseUser<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    <span class=\"token comment\"># ...<\/span>\n    <span class=\"token keyword\">class<\/span> <span class=\"token class-name\">Meta<\/span><span class=\"token punctuation\">:<\/span>\n        permissions <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">[<\/span>\n            <span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;can_access_admin&#x27;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token string\">&#x27;Can access the admin&#x27;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token comment\"># staff<\/span>\n            <span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;can_access_all_perms&#x27;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token string\">&#x27;Can access all permissions&#x27;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token comment\"># superuser<\/span>\n        <span class=\"token punctuation\">]<\/span>\n<\/code><\/pre>\n<p>Next is to add the properties for <code>is_staff<\/code> and <code>is_superuser<\/code>:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\">    <span class=\"token decorator annotation punctuation\">@property<\/span>\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">is_staff<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        <span class=\"token keyword\">return<\/span> self<span class=\"token punctuation\">.<\/span>has_perm<span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;users.can_access_admin&#x27;<\/span><span class=\"token punctuation\">)<\/span>\n\n    <span class=\"token decorator annotation punctuation\">@property<\/span>\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">is_superuser<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        <span class=\"token keyword\">return<\/span> self<span class=\"token punctuation\">.<\/span>has_perm<span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;users.can_access_all_perms&#x27;<\/span><span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>This then causes a few recursion issues with the <code>is_superuser<\/code> flag since there is a few places in the permission code which checks if the user is a superuser... hence the recursion! The first two places in <code>has_perm<\/code> and <code>has_module_perms<\/code> is to simply remove the code as it&#x27;s a short circuit.<\/p>\n<p>The trickier place to deal with the recursion is in the <code>_get_permissions<\/code> method of the authentication backend. At this point we grab the superuser permission and manually check if the user is either in a related group or directly related to the permission. This feels like a hack, but works for now which is this:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token keyword\">def<\/span> <span class=\"token function\">_get_permissions<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    <span class=\"token comment\"># ...<\/span>\n    super_perm <span class=\"token operator\">=<\/span> Permission<span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span>get<span class=\"token punctuation\">(<\/span>codename<span class=\"token operator\">=<\/span><span class=\"token string\">&#x27;can_access_all_perms&#x27;<\/span><span class=\"token punctuation\">)<\/span>\n    <span class=\"token keyword\">if<\/span> super_perm<span class=\"token punctuation\">.<\/span>group_set<span class=\"token punctuation\">.<\/span><span class=\"token builtin\">filter<\/span><span class=\"token punctuation\">(<\/span>user<span class=\"token operator\">=<\/span>user_obj<span class=\"token punctuation\">)<\/span> <span class=\"token keyword\">or<\/span> super_perm<span class=\"token punctuation\">.<\/span>user_set<span class=\"token punctuation\">.<\/span><span class=\"token builtin\">filter<\/span><span class=\"token punctuation\">(<\/span>pk<span class=\"token operator\">=<\/span>user_obj<span class=\"token punctuation\">.<\/span>pk<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        perms <span class=\"token operator\">=<\/span> Permission<span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span><span class=\"token builtin\">all<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n    <span class=\"token keyword\">else<\/span><span class=\"token punctuation\">:<\/span>\n        perms <span class=\"token operator\">=<\/span> <span class=\"token builtin\">getattr<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">,<\/span> <span class=\"token string\">&quot;_get_%s_permissions&quot;<\/span> <span class=\"token operator\">%<\/span> from_name<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">(<\/span>user_obj<span class=\"token punctuation\">)<\/span>\n    <span class=\"token comment\"># ...<\/span>\n<\/code><\/pre>\n<p>instead of:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token keyword\">def<\/span> <span class=\"token function\">_get_permissions<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    <span class=\"token comment\"># ...<\/span>\n    <span class=\"token keyword\">if<\/span> user_obj<span class=\"token punctuation\">.<\/span>is_superuser<span class=\"token punctuation\">:<\/span>\n        perms <span class=\"token operator\">=<\/span> Permission<span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span><span class=\"token builtin\">all<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n    <span class=\"token keyword\">else<\/span><span class=\"token punctuation\">:<\/span>\n        perms <span class=\"token operator\">=<\/span> <span class=\"token builtin\">getattr<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">,<\/span> <span class=\"token string\">&quot;_get_%s_permissions&quot;<\/span> <span class=\"token operator\">%<\/span> from_name<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">(<\/span>user_obj<span class=\"token punctuation\">)<\/span>\n    <span class=\"token comment\"># ...<\/span>\n<\/code><\/pre>\n<p>All of this is on a <a href=\"https:\/\/github.com\/nanorepublica\/django_groups_poc\/pull\/2\">PR<\/a> of the above repo if your interested. From here there are a couple of steps I would like to take:<\/p>\n<ol>\n<li>Package this up so it can be applied to an existing project. I&#x27;m taking inspiration from Carlton&#x27;s <a href=\"https:\/\/github.com\/carltongibson\/django-unique-user-email\">django-unique-user-email<\/a> PoC<\/li>\n<li>Experiment to see if we can drop the <code>is_staff<\/code> and <code>is_superuser<\/code> flags completely from the model. I&#x27;m not sure if this is practical, but an experiment no less. To note <code>is_staff<\/code> is used 4 times within Django and <code>is_superuser<\/code> is 3 times all of which were removed above.<\/li>\n<\/ol>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-176","title":"Using custom permissions for is_staff and is_superuser","summary":"Another experiment in slimming down the User model","date_modified":"2024-09-11T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#auth","#permissions"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-175","content_html":"<p>Today is a response to some of Carlton&#x27;s ideas in his latest <a href=\"https:\/\/buttondown.com\/carlton\/archive\/evolving-djangos-authuser\/\">Stack Report<\/a>. Go and have a read of that first, otherwise this doesn&#x27;t make much sense (It&#x27;s long one so get a coffee or tea!).<\/p>\n<p>Reading the above makes a lot of sense, as one of those that helps out on the Discord I cannot count the times beginners want to migrate to a custom user model or try to have multiple user models (one for each user type). When reading the article a few ideas popped into my head, some are new and some are an alteration of Carlton&#x27;s thoughts.<\/p>\n<p>First up is to bring Groups and Permissions into the fold rather than being an optional extra. This is particularly the case for the <code>is_staff<\/code> and <code>is_superuser<\/code> flags. I have previously demo&#x27;ed a <a href=\"https:\/\/github.com\/nanorepublica\/django_groups_poc\">PoC<\/a> for this and I want to extend this to integrate some custom permissions (eg <code>can_access_admin<\/code>, <code>has_all_permissions<\/code>). Look out for another post where I document this extension in detail.<\/p>\n<p>Second up is to document the broad steps needed to migrate from the default User model to a custom User model in the docs. Currently the docs state it&#x27;s difficult and should be avoided, along with toning down the language as Carlton suggests, we can outline the steps required to do the migration which lets developers make an informed choice.<\/p>\n<p>Third is a relationship that exists, but Carlton doesn&#x27;t address, which is the User relationship to permissions. This is a slightly spicy take, but I wonder if this can be removed? Inspired by this <a href=\"https:\/\/indiehackers.social\/@matthiask@hachyderm.io\/112992967482601054\">toot<\/a>, almost all the time you want to have a Group anyway and not apply permissions directly. My caveat here is if the <code>PermissionMixin<\/code> class changes significantly with other changes.<\/p>\n<p>Finally, the most spicy for last is the idle thought of having a swapable or required UserProfile model? Carlton suggests that a blank custom User is an anti-pattern, which prompted my to think then a blank user profile is a pattern to encourage?<\/p>\n<p>Essentially we end up with:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token keyword\">class<\/span> <span class=\"token class-name\">UserProfile<\/span><span class=\"token punctuation\">(<\/span>AbstractUserProfile<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    <span class=\"token keyword\">pass<\/span>\n<\/code><\/pre>\n<p>I do recognise that this introduces complexity back into Django and templates, and what about projects that don&#x27;t need a UserProfile etc. These are all valid concerns and really this idea is probably only suitable for Django 7 and it depends on how far the community allows the slimming down of the User model to go. The point of doing this is to encourage users towards not touching the User moddel, but instead create a UserProfile model. However I do recognise a &quot;how-to&quot; in the docs is a better first step and should be given time to be evaluated by the community and future beginners to Django.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-175","title":"Thoughts from the latest Stack Report","summary":"Adding my ideas to the mix for Django auth!","date_modified":"2024-09-10T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#auth","#permissions"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-174","content_html":"<p>Last Thursday and Friday I embarked on a quick hop over to Dublin to have a set of meetings with my new co-founder. We&#x27;re currently in stealth for now, but I will likely announce it publicly here first! Anyway&#x27;s apart from the very early start of 1am to get to Heathrow, the trip was a blast and I came away invigorated for this new venture.<\/p>\n<p>We covered the initial design and build for the product, using the &quot;Must, Should, Could, Won&#x27;t&quot; framework to force us to prioritise which features to build first as well as drafting the initial database design. Finally we discussed contracts, timelines and tools we would like to use. There was also chats over food and drink to further get to know each other.<\/p>\n<p>This week, I will be setting up some of those tools to enable remote collaboration, but right now I&#x27;m going to be experimenting with some ideas in Django until I really need to start building at the beginning of next year.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-174","title":"Dublin and back in 48 hour","summary":"Kick off meetings and the like!","date_modified":"2024-09-09T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#startups","#design","#prioritization","#tools"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-173","content_html":"<p>Today is a scary day, this post or should I say newsletter is now an email to the few who have signed up on my website! (Welcome by the way!) My platform of choice is <a href=\"https:\/\/buttondown.com\">buttondown.com<\/a> which has a handy RSS to email feature which is currently setup to create a newletter for each post I create, but there is lots more to explore!<\/p>\n<p>The other platform I recently added to the mix is Instagram where I am posting as @softwarecrafts. However that only gets a post when I include an image into my post. This is done using extending the zap I have that posts to Linkedin and Mastodon as well!<\/p>\n<p>Finally, a bit of a reintroduction is in order! I&#x27;m Andy, from Cambridge. I consult and build web software projects. When I do build I specialise in Python &amp; Django. For the most part I help startup&#x27;s go from zero to one, untangle a project and create processes to get it back on track or simply be a safe pair of hands to keep the project moving along. When I&#x27;m not working for clients, I like to climb, play games, help out at Scouts, participate in the Django community, hack on side projects and most importantly be with my wife and two sons.<\/p>\n<p>I post (or email) every weekday with short(ish) posts on Django and other bits of life. You can visit (softwarecrafts.co.uk)[softwarecrafts.co.uk\/100-words] to read the back catalogue. My email\/DMs is always open so feel to drop me a line!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-173","title":"Starting an email newsletter","summary":"One more platform for this content","date_modified":"2024-09-06T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#email","#meta","#instagram","#reintroduction"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-172","content_html":"<p>While setting up the studio, I was playing around with different camera positions and taking inspiration from Pete McAllen of @1pyramidpark where he filmed some of his studio work from above looking down on him. That&#x27;s the bonus shot of today! I recently upgraded my phone so my old phone is now attached to the ceiling using my existing setup with an extra mobile phone grip from Elgato, some extra power and a clip-on wide angle lens to capture what I think is quite a fun shot. I think it will make some great b-roll and timelapse content.<\/p>\n<p>The other thing I promised from yesterday is a complete list of items used in my setup. So here goes!<\/p>\n<ul>\n<li>Camo <a href=\"https:\/\/reincubate.com\/camo\/\">https:\/\/reincubate.com\/camo\/<\/a><\/li>\n<li>Elgato master mount: <a href=\"https:\/\/www.elgato.com\/ww\/en\/p\/master-mount-l\">https:\/\/www.elgato.com\/ww\/en\/p\/master-mount-l<\/a><\/li>\n<li>Elagto solid arm: <a href=\"https:\/\/www.elgato.com\/ww\/en\/p\/solid-arm\">https:\/\/www.elgato.com\/ww\/en\/p\/solid-arm<\/a><\/li>\n<li>Elgato phone mount (x2): <a href=\"https:\/\/www.elgato.com\/uk\/en\/p\/phone-grip\">https:\/\/www.elgato.com\/uk\/en\/p\/phone-grip<\/a><\/li>\n<li>Elgato key light: <a href=\"https:\/\/www.elgato.com\/ww\/en\/p\/key-light-mini\">https:\/\/www.elgato.com\/ww\/en\/p\/key-light-mini<\/a><\/li>\n<li>Elgato LED strips (newer version): <a href=\"https:\/\/www.elgato.com\/ww\/en\/p\/light-strip-pro\">https:\/\/www.elgato.com\/ww\/en\/p\/light-strip-pro<\/a><\/li>\n<li>Wash light: <a href=\"https:\/\/www.amazon.co.uk\/gp\/product\/B08CXRQPL9\/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&amp;th=1\">https:\/\/www.amazon.co.uk\/gp\/product\/B08CXRQPL9\/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&amp;th=1<\/a><\/li>\n<li>LED spot light: <a href=\"https:\/\/www.amazon.co.uk\/gp\/product\/B08X7CW6CS\/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&amp;psc=1\">https:\/\/www.amazon.co.uk\/gp\/product\/B08X7CW6CS\/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&amp;psc=1<\/a><\/li>\n<li>Second hand iPhone SE second edition<\/li>\n<li>Android phone (Fairphone 3 plus)<\/li>\n<li>Accousic panels - <a href=\"https:\/\/www.amazon.co.uk\/dp\/B0BL7CDYF9\">https:\/\/www.amazon.co.uk\/dp\/B0BL7CDYF9<\/a><\/li>\n<li>IKEA floor light - <a href=\"https:\/\/www.ikea.com\/gb\/en\/p\/harslinga-lunnom-floor-lamp-with-light-bulb-black-clear-glass-s39516960\/\">https:\/\/www.ikea.com\/gb\/en\/p\/harslinga-lunnom-floor-lamp-with-light-bulb-black-clear-glass-s39516960\/<\/a><\/li>\n<li>IKEA plant - <a href=\"https:\/\/www.ikea.com\/gb\/en\/search\/?q=FEJKA\">https:\/\/www.ikea.com\/gb\/en\/search\/?q=FEJKA<\/a> (the one I have isn&#x27;t available anymore!)<\/li>\n<li>SmallRig claws - <a href=\"https:\/\/www.amazon.co.uk\/dp\/B076VG1ZWY\">https:\/\/www.amazon.co.uk\/dp\/B076VG1ZWY<\/a><\/li>\n<li>SmallRig rods - <a href=\"https:\/\/www.amazon.co.uk\/dp\/B0093XN9XU\">https:\/\/www.amazon.co.uk\/dp\/B0093XN9XU<\/a><\/li>\n<li>SmallRig 90 degree rod clamps - <a href=\"https:\/\/www.amazon.co.uk\/dp\/B0776VFMZL\">https:\/\/www.amazon.co.uk\/dp\/B0776VFMZL<\/a><\/li>\n<li>SmallRig Magic arm - <a href=\"https:\/\/www.amazon.co.uk\/dp\/B09MVYKJW7\">https:\/\/www.amazon.co.uk\/dp\/B09MVYKJW7<\/a><\/li>\n<li>Wide Angle lens - <a href=\"https:\/\/www.amazon.co.uk\/dp\/B07F1SMC3G\">https:\/\/www.amazon.co.uk\/dp\/B07F1SMC3G<\/a><\/li>\n<li>Ceiling mounts x2 - <a href=\"https:\/\/www.amazon.co.uk\/dp\/B072HFG4K3\">https:\/\/www.amazon.co.uk\/dp\/B072HFG4K3<\/a><\/li>\n<li>Rod Clamps - <a href=\"https:\/\/www.amazon.co.uk\/dp\/B077MXKK27\">https:\/\/www.amazon.co.uk\/dp\/B077MXKK27<\/a><\/li>\n<li>Various USB C power cables and splitters for the phones and lights<\/li>\n<\/ul>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-172","title":"From Home Office to Home Studio - Bonus","summary":"An extra camera setup and the shopping list","image":"https:\/\/softwarecrafts.co.uk\/photos\/day-172.png","date_modified":"2024-09-05T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#office","#studio"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-171","content_html":"<p>Today we have my current setup which I don&#x27;t plan on changing anytime soon! The photo today has all the lights on and everything in it&#x27;s current position. The final additions are another feature light from IKEA and some fake greenery from IKEA as well (Links at the bottom). These were purchased and added to the setup again from the recommendations of the Dream Studio course. I also switched the picture frame for another that I had in my office, these of course will likely rotate over time.<\/p>\n<p>Other additions to this setup was to purchase some more SmallRig kit to secure the light above me to be more secure and earlier in the process I bought some accoustic panels to help with reverb.<\/p>\n<p>And that&#x27;s it! Today&#x27;s shot is how I would film a video or join a video call. In fact, I joined a virtual co-working session and fellow freelancer thought it was a virtual background, which I will take as the journey being worth it!<\/p>\n<p>Tomorrow is a little bonus with a complete list of the items used in the current setup.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-171","title":"From Home Office to Home Studio - Part 6","summary":"Finishing touches to the setup","image":"https:\/\/softwarecrafts.co.uk\/photos\/day-171.png","date_modified":"2024-09-04T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#office","#studio"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-170","content_html":"<p>I was doing yet more research from @thekevinshen of <a href=\"https:\/\/dreamstudio.co\">Dream Studio<\/a>, first with his free email course which had handy tips on lighting position, camera settings, inspiration and more! I also watched this <a href=\"https:\/\/www.youtube.com\/watch?v=MYlZS9JinjA\">video<\/a> which is of a space that is very similar to mine. Immediately one of Kevin&#x27;s suggestions stuck out which was to reposition the camera to shoot slightly diagonally down my office instead of pointing it towards the door which has frame cutting right through the shot vertically and impossible to move. That video also highlighted the need for some slight readjustments to my lighting.<\/p>\n<p>The other main learning from the free email course was framing myself in the shot, with the whiteboard removed I moved one of my picture frames to be in shot and a shelf that was just out of shot to better frame myself and use the LED strip to provide some subtle feature lighting.<\/p>\n<p>At this point we are 95% towards my current setup, there are a few more tweaks to the background as per a suggestion from Kevin&#x27;s course.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-170","title":"From Home Office to Home Studio - Part 5","summary":"More research... almost there!","image":"https:\/\/softwarecrafts.co.uk\/photos\/day-170.png","date_modified":"2024-09-03T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#office","#studio","#research"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-169","content_html":"<p>I had the black background for a while, but also living in a state of not being fully satisfied with it as a final solution, I also experimented with lights being positioned in other places (even outside...).<\/p>\n<p>This was especially when I saw what Justin Jackson (Transistor.fm) had done with his setup. He mentioned that he had paid for the <a href=\"https:\/\/dreamstudio.co\">dreamstudio.co<\/a> course by Kevin Shen and while I didn&#x27;t want to drop over $1000 on the course, I watched some of his content which included a few videos in which he setup a studio in a <a href=\"https:\/\/www.youtube.com\/watch?v=KN4MvxJHliM\">small space<\/a>. For context, the width of my office is just 2 metres.<\/p>\n<p>This research led me to two things. First that I needed a top light, which meant I needed some gear roughly above my head where I could attach a light. For this I got a couple of bits from SmallRig off Amazon to test this out and I was pleased with the result. Second the whiteboard had to be moved, it simply took up too much of the wall to be practical for a background to a professional looking video.<\/p>\n<p>Today&#x27;s shot is of these two actions being taken, a green top light and I have taken down the whiteboard.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-169","title":"From Home Office to Home Studio - Part 4","summary":"Research time","image":"https:\/\/softwarecrafts.co.uk\/photos\/day-169.png","date_modified":"2024-09-02T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#office","#studio","#research"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-168","content_html":"<p>The next upgrade to the office setup was more lights to create interest in the background, this was after watching a couple of youtube videos on the subject. For myself I purchased a wash light and a battery powered led colour light from amazon. I also removed half of the black backdrop as it covered the door creating too much faff to deal with. This also introduced some depth back into the shot, but I still wasn&#x27;t satisfied.<\/p>\n<p>Finally the other major upgrade came in the form of a old\/new camera. My co-mentor <a href=\"https:\/\/www.chrispaveymastering.com\/\">Chris<\/a> uses <a href=\"https:\/\/reincubate.com\/camo\/\">Camo Studio<\/a> and during one of our weekly calls I realised I had a spare iPhone lying around for mobile app testing. Camo for those that don&#x27;t know allows you to use a smartphone camera as a webcam, but it also allows full access to the camera settings such a resolution, shutter speed, exposure etc which means you can get the perfect high quality shot without needing shell out for a DSLR or a better webcam. I bet you likely have a spare old phone lying around. The other purchase that came with this was an elgato master mount with solid arm to ensure the phone was steady along with my key light.<\/p>\n<p>With all of the above in place you can see that the quality of the picture is there, but the background is still lacking, that starts to change in the next post.<\/p>\n<p>P.S. Here are the links to the hardware &amp; software for things I have bought to date:<\/p>\n<ul>\n<li>Camo <a href=\"https:\/\/reincubate.com\/camo\/\">https:\/\/reincubate.com\/camo\/<\/a><\/li>\n<li>Elgato master mount: <a href=\"https:\/\/www.elgato.com\/ww\/en\/p\/master-mount-l\">https:\/\/www.elgato.com\/ww\/en\/p\/master-mount-l<\/a><\/li>\n<li>Elagto solid arm: <a href=\"https:\/\/www.elgato.com\/ww\/en\/p\/solid-arm\">https:\/\/www.elgato.com\/ww\/en\/p\/solid-arm<\/a><\/li>\n<li>Elgato phone mount: <a href=\"https:\/\/www.elgato.com\/uk\/en\/p\/phone-grip\">https:\/\/www.elgato.com\/uk\/en\/p\/phone-grip<\/a><\/li>\n<li>Elgato key light: <a href=\"https:\/\/www.elgato.com\/ww\/en\/p\/key-light-mini\">https:\/\/www.elgato.com\/ww\/en\/p\/key-light-mini<\/a><\/li>\n<li>Elgato LED strips (newer version): <a href=\"https:\/\/www.elgato.com\/ww\/en\/p\/light-strip-pro\">https:\/\/www.elgato.com\/ww\/en\/p\/light-strip-pro<\/a><\/li>\n<li>Wash light: <a href=\"https:\/\/www.amazon.co.uk\/gp\/product\/B08CXRQPL9\/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&amp;th=1\">https:\/\/www.amazon.co.uk\/gp\/product\/B08CXRQPL9\/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&amp;th=1<\/a><\/li>\n<li>LED spot light: <a href=\"https:\/\/www.amazon.co.uk\/gp\/product\/B08X7CW6CS\/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&amp;psc=1\">https:\/\/www.amazon.co.uk\/gp\/product\/B08X7CW6CS\/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&amp;psc=1<\/a><\/li>\n<\/ul>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-168","title":"From Home Office to Home Studio - Part 3","summary":"More lights, new camera and tried my first video record","image":"https:\/\/softwarecrafts.co.uk\/photos\/day-168.png","date_modified":"2024-08-30T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#office","#studio","#lighting","#camo_studio"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-167","content_html":"<p>Yesterday I introduced my recent journey of upgrading my office for video recording and calling. The first issue I had was covering the whiteboard in the background as it reflected too much light, as well as creating some designed interest in the background with some lighting.<\/p>\n<p>This first attempt involved buying some black table cloth and blackout blinds to hang behind me using some hooks and eyelets. This worked reasonable well in covering the whiteboard, but it had one fatal flaw, there was no depth to the picture as can be seen from today&#x27;s picture, as well as being a bit of a faff to deal with at times.<\/p>\n<p>In terms of lighting I purchased an Elgato key light mini to light my face along with a Benq E-reading light that I already owned and some Elgato light strips with magnetic tape to attach them to the whiteboard since that was magnetic. It was a good first attempt and I stuck with this backdrop for a quite a period of time while I tweaked other elements of the setup, which we will see tomorrow.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-167","title":"From Home Office to Home Studio - Part 2","summary":"First attempt at backdrops and lights","image":"https:\/\/softwarecrafts.co.uk\/photos\/day-167.png","date_modified":"2024-08-29T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#office","#studio","#video"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-166","content_html":"<p>One of my goals for my business is to produce videos around Django that serve the community. I have had this goal for well over a year especially with quite a few video tutorials reccommending poor practices. My office, however was setup for me working and the occasional video call, not for producing videos on a regular basis. I didn&#x27;t feel comfortable even attempting to record a video with that setup.<\/p>\n<p>Over the last 18 months I have gone through various iterations of backdrops, adding lighting, upgrading my camera and other bits of research to get to what I have today.<\/p>\n<p>Over the next few days, I am going to step through each iteration and explain what I did and why. As for the video production, I need to understand the recording, editing and publishing process until I am ready to move forward, but perhaps some shorter experiments will arrive in the meantime.<\/p>\n<p>Today&#x27;s photo is where I started before doing any work!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-166","title":"From Home Office to Home Studio","summary":"Upgrading my office for video calling and recording","image":"https:\/\/softwarecrafts.co.uk\/photos\/day-166.png","date_modified":"2024-08-28T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#office","#studio","#video"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-165","content_html":"<p>I have just spun up a new Django repo for a new startup that I will be embarking upon over the next few months. Hopefully I will be living with this codebase for the next years if not longer, so I am trying to take the time at the beginning to set the groundwork for non-functional requirements, putting in place code patterns for each maintenance and establishing how features will be built.<\/p>\n<p>Currently the stack is as follows (based on the Django Cookiecutter template):<\/p>\n<ul>\n<li>Django<\/li>\n<li>Postgres<\/li>\n<li>Redis (caching and queue)<\/li>\n<li>Celery<\/li>\n<li>TailwindCSS (TailwindUI)<\/li>\n<li>HTMX<\/li>\n<li>Alpine? (I know is has CSP issues)<\/li>\n<\/ul>\n<p>Then I&#x27;m going to use Neapolitan for views, have a look at postgres triggers, really think about permissions (I think I might need to redo my User model to remove user_permissions). Base models, creating a solid audit trail of changes (and integrating that into the admin) and all this without hopefully complexity ballooning beyond control!<\/p>\n<p>It honestly feels a bit overwhelming to think everything through from scratch, I normally pick up an existing codebase from clients then make fixes and improvements. That said, I am excited by this opportunity to pour my learnings from recent years into a single codebase and throughly explore using HTMX with Django to it&#x27;s fullest potential.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-165","title":"Starting a new project","summary":"Building a Django project for long term success","date_modified":"2024-08-27T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#greenfield","#startup"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-164","content_html":"<p>When not doing activities with the family this holiday, in the small brief quiet moments, I have been slowly clearing out my backlog of newsletters that I have received in the past and I have been meaning to read for an age. That time has been this holiday! So far I have cleared out over a 100 emails which leaves just under 250 emails for me to go (I probably won&#x27;t get to zero as some require action from me in the next couple of weeks).<\/p>\n<p>All that said it has been great to have a bit of a digital declutter and delve into some interesting topics that have been sitting there for a while. Two newsletters in particular, that I would like to highlight is <a href=\"https:\/\/www.freelancecake.com\/newsletter\">Freelance Cake<\/a> from Austin Church who has a way with words to get me thinking about my business in a new light. The other is <a href=\"https:\/\/alexanderobenauer.com\/labnotes\/000\/\">Lab Notes<\/a> from Alexander Obenauer who is exploring what it would look like to redefine what personal computing should look like in the future (or the present as he appears to be building his vision already!)<\/p>\n<p>I enjoy this stuff for a few main reasons, firstly I geninuely enjoy, second I always have my current and possible future clients in the back of my mind when consuming any knowledge and if it might apply to them. I then pass on the email, article etc to them for them to level up with my take on how it might work for them. This is part of what I give to every client that works with me, not just a tick box in each task, but I look beyond the task list to the business problem being solved.<\/p>\n<p>I&#x27;m pretty full right now, but drop me a line if you have a project that needs some advice or a sounding board for a decision to make.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-164","title":"Inbox Declutter","summary":"Holiday Musings - 5","image":"https:\/\/softwarecrafts.co.uk\/photos\/day-164.jpg","date_modified":"2024-08-26T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#holiday_musings","#reading","#inbox"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-163","content_html":"<p>Today was a day trip to Straford-upon-Avon which is famously home to all things William Shakespeare, we did have a wander around the town to see some of the sights. But the other thing that Stratford is home to is the UK&#x27;s largest butterfly farm, which was just amazing to walk through. having what must have been hundreds of butterflies fluttering and flittering around you is quite magical and wonderful. ]<\/p>\n<p>Once again I had my camera out to capture the beauty of them and also playing around with the slo-mo functionality to capture the brilliant speed of the wings. I do enjoy capturing nature, it&#x27;s the simplicity of the lives of other animals compared with the complexity of our own and wonderful visual display of colour and geometry that captures my mind and heart which slows me down to just enjoy the moment.<\/p>\n<p>If you are ever in the area of passby another butterfly farm do give it a visit.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-163","title":"Holiday Musings - 4","summary":"Shakespeare and butterflies","image":"https:\/\/softwarecrafts.co.uk\/photos\/day-163-1.jpg","date_modified":"2024-08-23T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#holiday_musings","#butterflies"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-162","content_html":"<p>Normally when we talk about the securing a Django project the focus is on adding certificated to enable HTTPS to the client side of the project, which is sensible since that side of the application is going to most likely communicate over the public internet. However the connection to the database from Django is by default in plaintext. Let&#x27;s briefly cover how to achieve communicating over SSL with postrges (my database of choice) and Django.<\/p>\n<p>There are three aspects to the setup:<\/p>\n<ol>\n<li>Generate or acquire the certificates<\/li>\n<li>Configure postgres to start with SSL enabled<\/li>\n<li>Configure Django to use SSL mode<\/li>\n<\/ol>\n<p>Let&#x27;s take each step at a time. Using a hosted database like RDS will provide the certificate to download and add to the codebase. For a local setup then I recommend generating certificates using <code>mkcert<\/code>.<\/p>\n<p>Next configuring postgres for a hosted instance will vary, for example with RDS, modifying the database to select the certificate will to the job. When working with Docker you can use the following Dockerfile to enable SSL:<\/p>\n<pre><code>FROM postgres:latest\n\nCOPY .\/compose\/local\/mkcert\/localhost\/postgres.pem \/var\/lib\/postgresql\/server.crt\nCOPY .\/compose\/local\/mkcert\/localhost\/postgres-key.pem \/var\/lib\/postgresql\/server.key\nRUN chmod 600 \/var\/lib\/postgresql\/server.key &amp;&amp; \\\n    chown postgres \/var\/lib\/postgresql\/server.key\n\nCMD [&quot;-cssl=on&quot;, &quot;-cssl_cert_file=\/var\/lib\/postgresql\/server.crt&quot;, &quot;-cssl_key_file=\/var\/lib\/postgresql\/server.key&quot;]\n<\/code><\/pre>\n<p>The above makes some assumptions on the name and location of the certificates on the host. The key thing is the arguments on the last line.<\/p>\n<p>Finally to configure Django we need to add one or two options to the <code>DATABASES[&quot;OPTIONS&quot;]<\/code>. The first is <code>sslmode<\/code> which needs to be set to one of <code>require<\/code>, <code>verify-ca<\/code> or <code>verify-full<\/code>, then depending on the certificate used <code>sslcert<\/code> or <code>sslrootcert<\/code> needs to be specified with the path to the certificate for Django to use.<\/p>\n<p>Restarting everything should result in Django and Postgres communicating over SSL. If I have time I will add an example repo for this setup.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-162","title":"Securing a Django project end-to-end","summary":"Securing the database conection as well as the client connection","date_modified":"2024-08-22T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#postgres","#ssl"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-161","content_html":"<p>The mention of driving the other day, reminded me that I often come back to a startup idea that I often talk about in-person. I am lucky enough to not require a car, we live in a city where I can cycle most places I need and when I do need a car then I can hire one from <a href=\"https:\/\/www.enterprisecarclub.co.uk\/gb\/en\/home.html\">Enterprise Car Club<\/a> which fills the need I have, but could also do with some improvements to be amazing (at least from my point of view). The other half of the idea comes from <a href=\"https:\/\/rippleenergy.com\/\">Ripple Energy<\/a> who set up co-operatives that allow an individual to invest and own part of a wind or solar farm and then make savings on your energy bill.<\/p>\n<p>So to the idea. This company vision would be to kill the ownership of the second household car, then go for the primary car. Why the second car in a household? Ask most people and they would probably say they would rather not have it, cars are a very (perhaps most) convenient travel option available, but they cost lots to run and maintain and rarely make a profit in terms of an asset. The second car is there for the times when schedules clash, otherwise it sits in the driveway collecting dust, which is most of the time.<\/p>\n<p>That&#x27;s the why, the how it to provide a system for a group of households to create a co-operative (inspired by Ripple) to own a tiny fleet of cars (I would imagine it to be 1 or 2 cars most of the time), inspired by Enterprise Car Club or Zipcar. The company would provide the legal and financial setup for the cooperative, the IT infrastructure for managing the cars, the insurance for multiple people to use the cars via the cooperative, the hardware for accessing the keys, helping to deal with fuel and mileage. The company would take an initial setup fee and then a monthly management fee. I would also expect most (if not all) of the work to be open source, for communities or governments to self-host it. But the company would have the expertise and could possibly restrict some functionality. The final portion of the idea would be acquiring the unused cars for those that join the cooperative and reusing or recycling them appropriately, I have yet to think through this completely.<\/p>\n<p>I have sat on the above for far too long and pretty much everything I speak to thinks it&#x27;s a good idea (might mean it fails the Mom Test), but given the current climate state of the world, I think it has a shot of working, but I am unlikely the person to make it happen since I am about to start my next venture which is going to keep me busy for the next decade (no joke), someone take this and make it work, please!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-161","title":"Killing the second car","summary":"A startup idea that has been in my head for a while","date_modified":"2024-08-21T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#idea","#startups","#venture_capital"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-160","content_html":"<p>Today we visited a National Trust garden around the corner from where we are staying and visiting these places always reminds me that I actually enjoy getting out the smartphone camera and snapping some photos. Recently I upgraded to a Fairphone 5 which has a wide angle lens which I am really enjoying. In particular I love to go super close up to plants and do some macro photography.<\/p>\n<p>Recently I bought a smartphone gimbal from DJI and while I am do want to use it more, I am still getting used to it and it does feel like a bit too much faff to get it out and running for a quick video (especially when you have a baby and 6 year old competing for my attention!). That said I captured a couple of short videos that I like from today, essentially b-roll but I love experimenting with framing and motion when it comes to video.<\/p>\n<p>See above for some of today&#x27;s shots!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-160","title":"Holiday Musings - 2","summary":"Enjoying photography and starting videography","image":"https:\/\/softwarecrafts.co.uk\/photos\/day-160-1.jpg","date_modified":"2024-08-20T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#holiday_musings","#photography","#videography"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-159","content_html":"<p>This begins a possible detour from my normal writings, I am on holiday with the family for two weeks, which means giving client work a break (assuming no fires need to be put out etc). However I did want to keep writing, 10 days of not writing would definitely kill a habit I have worked hard to keep up until this point. Therefore my writings over the next 10 days might be a little less technical or I might use some non-technical observation to have potentially new take on a technical topic. So without further ado...<\/p>\n<p>Today I was a fair amount of driving, especially when we as a family don&#x27;t own a car (we hire cars when the need arises) and most of this driving was on dual carriageways or motorways. Two of my annoyances when driving is those drives that stick in the middle lane and lorry drivers who overtake another lorry. I fully believe driving is an active activity in this context although it is very easy to slip into a passive mindset of letting the miles slip by. Additionally while lorry drivers are being active in performing an overtake it is so slow for them to accelerate safely that they cause a small line of traffic behind them.<\/p>\n<p>Both of these situations, each demonstrating the exact opposite behaviour (passive vs active), show a lack of context for the wider environment or system the reside in. You could make the link that each of these situations link to poor performers in a team, or perhaps a different representations of technical debt that could exist in a codebase (if you would like to hear these let me know). But while I write this, I am reminded that knowing the context is king in pretty much every situation in life, be it driving a car, debugging some software or having a simple conversation and without it, you are likely to make a situation worse for everyone involved including yourself.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-159","title":"Holiday Musings - 1","summary":"Still writing while on holiday","date_modified":"2024-08-19T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#meta","#holiday_musings"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-158","content_html":"<p>Yesterday was was the discussion of putting together a job description. Now that exists, what next? Well then it is the case of advertising the role. There are a few options available to you: recruiters, freelance sites, private communities and your network, both online and offline.<\/p>\n<p>First recruiters, generally, I would avoid them and lean on your own network if you can. Not all of them are bad, but you are more likely to encounter someone who is going to spam the wrong potential candidates that will not meet your needs and will cost far more than you have budget for right now. The exception here is if you know one personally or have been recommended someone from a trusted friend.<\/p>\n<p>Freelance sites are a good place if you need something done, but you are less likely to find a long-time technical partner when using these sites. The other advantage is there is going to exist some level of prequalification that these sites have, either through a rating system or the process that they have to onboard the talent. My recommendation is to go for one of these once you have exhausted other options.<\/p>\n<p>Personally the best place to advertise is in your network, be that Linkedin or other social networks, but also private communities that you may belong to. I see these communities as places where the signal from responses is going to be much higher than the noise that you will get from say LinkedIn.<\/p>\n<p>Depending on where it&#x27;s advertised, some or a lot of applications will come in. How does one sort through them to get to the best candidates?<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-158","title":"Hiring a developer part 3","summary":"Advertising the role","date_modified":"2024-08-16T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#hiring"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-157","content_html":"<p>This is a follow on from <a href=\"\/100-words\/day-148\">day 148<\/a>, and assuming you have followed that post, we&#x27;re now at the point where you have some revenue in the bank to spend on hiring a developer. The question then becomes what are they going to build, how are they going to build it and do I need to know all these buzzwords that technical people go on about?<\/p>\n<p>The first step is write a job description of what you need this technical person to do. The first thing to accept is that you what you think needs to be built is unlikely what will actually get built. To that end focus on communicating the vision of what you want built and the culture you want to have with this hypothetical engineer. This means communicating the values you want for the business. You want them to bought into why this business needs to exist, you want them to be a partner on this journey.<\/p>\n<p>The other important points to specify are the ability for this engineer to clearly explain technical concepts without huge amount of jargon, prototyping ideas in a short space of time and keeping focus on the business objectives (it&#x27;s all too easy for engineers to get shiny object syndrome with technology). Related to that last aside, you want the engineer to pick &#x27;boring&#x27; technology, generally this means it has existed for at least 5 years at a minimum in not more (generally the older, the better.) and that they have plenty of experience in it as well.<\/p>\n<p>Take your time on this part and do not be afraid to come back and revise it if you don&#x27;t feel like you are talking to the right people. It will be painful, but worth it to get the right person working with you. Next up is putting the job role out into the world!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-157","title":"Hiring a developer part 2","summary":"Crafting the role you need","date_modified":"2024-08-15T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#hiring","#job_descriptions","#job_roles"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-156","content_html":"<p>I have played around with permissions in various forms whether it be in a Django project or administering something like a Google Workspace domain and I have picked up a few rules along the way to make maintainence easier over the long haul.<\/p>\n<p>First up is to only ever apply permissions to a Group and never to an individual user. This applies even if there is a single person in a group. This is a bit of extra work when you are starting out, but saves plenty of time in future when either wanting a new user with the same permissions or moving permissions around to a different user if a role changes.<\/p>\n<p>Next up, when possible is to create two layers of groups. The first group represent teams of people, for example marketing, engineering or sales. These are the groups that users get added to them. The second layer of groups represent the roles for users to perform. These groups are linked to the perimissions that users need to have or other resources they need to represent. This second layer is only allowed to have the first layer of groups to as members. The benefit here is that users will inherit functionality based on their team groups rather than needing to be added to many more permission based groups. This level of hierachy is not native to Django so would require some custom implementation details.<\/p>\n<p>Finally, as a developer, I typically ensure that Groups and permission assignment happens as a programmatic process within the application rather than having a manual process for it. This is outline as a option in the Django docs, but perhaps isn&#x27;t the first default action to take since they are existing models. Groups can be limited in what you can add to them directly but there is nothing to stop you from having a GroupProfile model (similar to a UserProile) model with a OneToOne relationship to Group. Essentially treat them like normal models.<\/p>\n<p>I hope these tips bear fruit if you need to implement permissions and groups in your work.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-156","title":"Recommendations for working with Django Groups and Permissions","summary":"Some default patterns I consider when dealing with permissions","date_modified":"2024-08-14T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#auth","#permissions","#groups"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-155","content_html":"<p>I was recently helping a couple of people on the Django discord, where they had appeared to have switched a primary key for a model midway through a project. After a little experimenting this morning I am not entirely sure how they got into the state they did, but either way I feel like there might be some room for Django improving primary key migrations.<\/p>\n<p>First it is worth noting that to properly migrate to a new primary key the following operations need to happen:<\/p>\n<ol>\n<li>Create a new model with the new primary key defined (apply this migration)<\/li>\n<li>Create duplicate relationship fields on all other models that reference the old model to reference the new model<\/li>\n<li>Create a data migration to copy data over from the old model to the model<\/li>\n<li>Create a data migration to copy the relationship data from the old model references to the new model references.<\/li>\n<li>Finally drop the old relationships and old models once the above has been deploy and verified to work.<\/li>\n<\/ol>\n<p>This approach while cumbersome is required to maintain data integrity, generally I try to avoid changing the primary key unless absolutely necessary.<\/p>\n<p>The second thing I found interesting was that the Django migration system allowed me to add a primary key field to an existing model without so much of a hiccup until things start to crash and burn after the migration has been applied. This makes me wonder if the <code>AlterField<\/code> should check for the <code>primary_key<\/code> keyword argument and either error out or show a warning?<\/p>\n<p>What do you think about this? Have I missed anything, skimming some open Trac tickets suggest I might have and I admit I am not database expert and the above approach certainly wouldn&#x27;t scale well at all to a large table. I do feel a warning would be appropriate though.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-155","title":"Changing the Primary Key on a Django Model","summary":"It is not an easy switch","date_modified":"2024-08-13T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#models","#migrations","#primary_keys"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-154","content_html":"<p>Last week was the final week of Djangonaut Space Session 2 and it was a blast to be a Djangonaut! As a small reminder the goal of the program is to create more contributors to Django and it&#x27;s wider ecosystem of packages. From my personal experience that has certainly been the case. During the last 8 weeks I have achieved the following:<\/p>\n<ul>\n<li>Become familiar with Trac, the issue tracking system for Django<\/li>\n<li>Claimed 3 tickets to work on<\/li>\n<li>Proposed a new change in the forum, which led to me opening a new ticket, which was accepted, then I worked on it myself.<\/li>\n<li>Opened 4 PRs, 3 of them are now merged.<\/li>\n<li>Have a contribution in Django 5.1 and will have more in 5.2.<\/li>\n<li>Contacted the DSF about a new working group idea.<\/li>\n<\/ul>\n<p>I call that a success my any measure and I look forward to contributing in future. The program was the motivation and encouragement to just get started, from the application form asking me to do the contributing tutorial, to the weekly calls where I wanted to have something to talk about and progress made.<\/p>\n<p>When applications for the next session get announced, I highly recommend anyone apply who wants to get more involved in Django.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-154","title":"Djangonaut Space Session 2 has finished!","summary":"Looking back on the last 8 weeks","date_modified":"2024-08-12T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#djangonaut_space","#contribution","#mentoring"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-153","content_html":"<p>Plenty of times I have seen something like this shared in the Django Discord.<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token keyword\">def<\/span> <span class=\"token function\">my_view<\/span><span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    <span class=\"token keyword\">try<\/span><span class=\"token punctuation\">:<\/span>\n      <span class=\"token comment\"># some code<\/span>\n    <span class=\"token keyword\">except<\/span> Exception <span class=\"token keyword\">as<\/span> e<span class=\"token punctuation\">:<\/span>\n      <span class=\"token keyword\">return<\/span> JsonResponse<span class=\"token punctuation\">(<\/span><span class=\"token builtin\">str<\/span><span class=\"token punctuation\">(<\/span>e<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span> status<span class=\"token operator\">=<\/span>status<span class=\"token punctuation\">.<\/span>HTTP_500_INTERNAL_SERVER_ERROR<span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>This not the right thing to do in anyway shape of form. 99% of the time the 500 status code should be reserved for unhandled errors. Therefore catching a blanket exception and returning a 500 response means you will miss valuable information while developing your Django project and when deployed with <code>DEBUG=False<\/code> then there is the <code>handler500<\/code> view which will return if a 500 is triggered so end users see a reasonable error page (which can also be customised).<\/p>\n<p>When errors are handled then the 500 should never be returned. By the simple fact that the error is being handled means you as a developer know what is going wrong to some extent, then the appropriate HTTP status code would either be in the 2XX or more likely 4XX range. Then the client (browser) can handle that request appropriately, but intentionally returning a 500 means that the server has errored which runs counter to a well run Django server.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-153","title":"Understanding the 500 HTTP Status Code in Django","summary":"What not to do and, what is available.","date_modified":"2024-08-09T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#error_handling"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-152","content_html":"<p><a href=\"\/100-words\/day-149\">Day 149<\/a> saw me experimenting with the idea of using the Django <code>Group<\/code> model to replace the boolean fields for <code>is_staff<\/code> and <code>is_superuser<\/code>. Today I continued the quick experiment to the <code>is_active<\/code> flag and it turns out that it&#x27;s very similar code with a similar level of edits to the other flags.<\/p>\n<p>Like with the other flags we needed to add a python property to our User model to check for group membership. Next was creating the group via a data migration, a small edit to the auth backend and finally updating our User manager to add new users to the group when they are created (this could be moved however based on the sign up flow).<\/p>\n<p>If you want to see this in action, I created a PR for it on my <a href=\"https:\/\/github.com\/nanorepublica\/django_groups_poc\/pull\/1\/files\">prototype repo<\/a>. I wonder if this can be converted into a reusable app to make it easy to adopt ... the main tricky thing would be dealing with the fields since they exist on the default User model... but that&#x27;s a post for another day.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-152","title":"Using a Group for the is_active flag","summary":"Extending on an idea","date_modified":"2024-08-08T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#permissions","#auth"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-151","content_html":"<p>The concept of a &#x27;role&#x27; seems to be partially absent from the Django auth app. I am sure there was or still is a valid reason for this absence back when the original authors first concieved. That said, with my recent prototype last week (see <a href=\"\/100-words\/day-150\">day 149<\/a>), it made me wonder if the concept of roles could easily be added to Django without too much added complexity.<\/p>\n<p>This would enable Django to have a full <a href=\"https:\/\/en.wikipedia.org\/wiki\/Role-based_access_control\">RBAC<\/a> system. The missing piece currently is that it isn&#x27;t possible to have hierarchy. The simple mention of a hierarchy make me think of tree data structures and the simple realisation that this probably why it is absent from Django core!<\/p>\n<p>My initial thought would be if a ManyToManyField could be added to the Group model which points to itself. Here the concept of roles and groups are very similar, but ought to be distinct. A Group is a group of people (eg A set of staff users), the role is the actions that can be performed for a level of responsibility. Currently both of these are merged into the single Group object. Adding the ManyToManyField would be as follows:<\/p>\n<pre class=\"language-diff\"><code class=\"language-diff\">class Group(models.Model)\n<span class=\"token unchanged\"><span class=\"token prefix unchanged\"> <\/span><span class=\"token line\">   name = models.CharField(_(&quot;name&quot;), max_length=150, unique=True)\n<\/span><span class=\"token prefix unchanged\"> <\/span><span class=\"token line\">   permissions = models.ManyToManyField(\n<\/span><span class=\"token prefix unchanged\"> <\/span><span class=\"token line\">       Permission,\n<\/span><span class=\"token prefix unchanged\"> <\/span><span class=\"token line\">       verbose_name=_(&quot;permissions&quot;),\n<\/span><span class=\"token prefix unchanged\"> <\/span><span class=\"token line\">       blank=True,\n<\/span><span class=\"token prefix unchanged\"> <\/span><span class=\"token line\">   )\n<\/span><\/span><span class=\"token inserted-sign inserted\"><span class=\"token prefix inserted\">+<\/span><span class=\"token line\">    groups = models.ManyToManyField(&#x27;self&#x27;, blank=True, related_name=&#x27;roles&#x27;)\n<\/span><\/span><\/code><\/pre>\n<p>The general intention is that &#x27;role&#x27; groups have permissions and have groups while &#x27;group&#x27; groups only have users (via the reverse M2M on PermissionMixin)<\/p>\n<p>Another route would be to have a separate <code>Role<\/code> model, but that feels like a more significant change. Anyway thinking about this change for writing this blog post makes me realise why this wasn&#x27;t done in the first place! Things get complex quickly!<\/p>\n<p>What do you think? Should Django have a separate roles for permissions in some form?<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-151","title":"Idea: Adding minimal Roles to Django","summary":"Groups are not the same as a Role","date_modified":"2024-08-07T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#contribution","#new_feature_proposal"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-150","content_html":"<p>When working for yourself it is always a struggle to celebrate the wins as there is always &#x27;more to do&#x27;. Be it creating more content, finding new clients, improving systems and also just doing the work you have been paid to do.<\/p>\n<p>So today I am taking a small step back to celebrate two things! First I have just passed being in business for 5 years!\nThis last year has gone incredibly fast, with a couple of recent new clients coming by in the last month or so. Otherwise it&#x27;s been a quieter year which has allowed me to finish setting up my office as a place to happily record videos and has allowed me to start this habit for writing a lot more! The important lesson from this year has been know the value of building a cash reserve so that slow months didn&#x27;t put me out of business.<\/p>\n<p>Speaking of which today is day 150 of writing each weekday. I have been at this for 7 months and only recently have I found it tougher to find the content. It is surprising to me the variation of how much preparation is needed for some of the content. Each article consumes a fair amount of brain space to get the tone as I would like and make sure I communicate something useful. I hope to expand the platform mediums that this content goes onto, specifically I am experimenting with audio first, then I will be looking at video - watch this space!. Here&#x27;s to the next milestone - day 200 which will be towards the end of this year.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-150","title":"5 years and 150 posts","summary":"5 years in business, 150 posts into this habit","date_modified":"2024-08-06T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#meta","#wins"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-149","content_html":"<p>This little prototype was inspired by a conversation on Discord last week. Someone wanted to do away with the <code>is_staff<\/code>, <code>is_superuser<\/code> and <code>is_active<\/code> flags on the user and replace them with <code>Group<\/code> instances. While I wouldn&#x27;t recommend doing this by default, I could see this being a good way to introduce using Django&#x27;s permission system instead of it being ignored by default and some hack of a permission system based on attribute &#x27;truthyness&#x27; is used instead.<\/p>\n<p>Anyway, if you wanted to replace <code>is_staff<\/code> and <code>is_superuser<\/code>, which could easily be argued that they should be groups. (<code>is_active<\/code> is a little more vague as to whether it&#x27;s a property of the user or could be a group named <code>active users<\/code>). To achieve this you would need to do the following:<\/p>\n<ul>\n<li>Create Custom User from <code>AbstractBaseUser<\/code> and copy most of the contents from <code>PermissionMixin<\/code> into it.<\/li>\n<li>A Custom UserManager, to add users to the relevant group based on the right flag.<\/li>\n<li>Custom Authentication ModelBackend (the <code>is_superuser<\/code> is queried as a field)<\/li>\n<li>Add <code>is_staff<\/code> and <code>is_superuser<\/code> as properties to the custom User model to return based on querying the group name.<\/li>\n<\/ul>\n<p>And that&#x27;s it! The PermissionMixin copy is perhaps the most frustrating thing about it.<\/p>\n<p>Here is a <a href=\"https:\/\/github.com\/nanorepublica\/django_groups_poc\">proof of concept<\/a> if you want to see the code itself in a working state. I doubt this would ever happen in Django core, so feel free to fork it or copy for a project where you want to focus on group permissions.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-149","title":"Using Groups for the staff and superuser flags","summary":"A prototype for a possibly more aligned authentication system","date_modified":"2024-08-05T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#groups","#authentication"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-148","content_html":"<p>I often have chats with non-technical folk who want to build an app or start a buiness and they need some software made for it to exist. What most fail to graps at least initially is how expensive it is to build software and then maintain it. Essentially you want to postpone spending money on a developer for as long as you can until you bring in revenue.<\/p>\n<p>Therefore I recommend non-technical folk try to do the following first:<\/p>\n<ol>\n<li>Validate the idea\na. Build a no-code marketing website and refine the copy to create a waitlist or ideally presell the idea.\nb. Use no-code tools to build the smallest, first version to test the concept before investing in custom code\nc. Use marketing channels (ads, cold calling etc) first to see if there is demand for the idea.<\/li>\n<li>Build up some cash reserves\na. Developers are expensive, whether that be freelancers, consultants, agencies or permanent employees. Therefore plan for this.<\/li>\n<li>Have a solid tested idea for revenue - ideally this would be recurring in some way.<\/li>\n<li>Think about spending money on copywriters, marketers and designers before you get to a developer.<\/li>\n<\/ol>\n<p>When you are ready to hire a developer, do consider hiring a designer as well if you can afford it! First look for a full-stack developer that has been around for a while, at a minimum 5 years at this point. If they are good then they will be worth the investment. Finally do consider putting some equity on the table, this won&#x27;t appeal to everyone (we do have bills to pay too!), but some might like the potential upside if your business plan is solid.<\/p>\n<p>I will personally I won&#x27;t work with someone who hasn&#x27;t tried these first. I don&#x27;t want to be responsible for unnecessarily draining someone bank account. That said I am always happy to discuss an idea and give my critical thoughts to it. In fact, that&#x27;s my bread and butter, drop me a line if you want to chat.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-148","title":"A short guide to hiring developers","summary":"When is right to make the leap to hire a developer?","date_modified":"2024-08-02T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#hiring","#start_a_business"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-147","content_html":"<p>Earlier this week I walked though the options of deployments for Django. The default mode for a lot of these setups is either clicking buttons in a browser or typing commands into a terminal. This is perfectly fine for that side project or learning something new. However it doesn&#x27;t quite work when a setup has been running for a number of months with modifications along the way for the server to crash or something to fail.<\/p>\n<p>This is where a tool like Terraform comes into play. Terraform codifies an infrastructure setup from AWS instances to DNS records or Github repos and then allows you to replay it anytime you like. Terraform code can then be commited to a git repo like any other code. Using a tool like this ought to be a default for any serious project in 2024, but it typically isn&#x27;t in smaller companies. As a developer start using a tool like this as soon as you can, it means your infrastructure history is tracked which is key for knowing who changed what and for being able to recover from an outage swiftly and reliably.<\/p>\n<p>This kind of thing is tricky to know about up front, especially when there is so much else to do and learn. That&#x27;s where I come in. I can help you get up to speed quicker on your specific problems or give you some insight on what to do now versus later. Drop me an email and I&#x27;ll be in touch.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-147","title":"Django deployments - Bonus part","summary":"How to remember what you have setup","date_modified":"2024-08-01T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#terraform","#deployment"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-146","content_html":"<p>It&#x27;s been a few weeks since I had my first ticket accepted and someone else claimed it, but they haven&#x27;t progressed it at all, therefore today I claimed the ticket for myself and started work.<\/p>\n<p>The ticket is <a href=\"https:\/\/code.djangoproject.com\/ticket\/35591\">35591<\/a> and adds a warning to runserver against it&#x27;s usage in a production setting. Before I started I outlined a list of tasks required before getting to work. This is was the task list:<\/p>\n<ul>\n<li>add flag to runserver command<\/li>\n<li>add new message with flag to hide it<\/li>\n<li>update output at \u200b<a href=\"https:\/\/docs.djangoproject.com\/en\/5.0\/intro\/tutorial01\/#the-development-server\">https:\/\/docs.djangoproject.com\/en\/5.0\/intro\/tutorial01\/#the-development-server<\/a><\/li>\n<li>update reference docs: \u200b<a href=\"https:\/\/docs.djangoproject.com\/en\/5.0\/ref\/django-admin\/#runserver\">https:\/\/docs.djangoproject.com\/en\/5.0\/ref\/django-admin\/#runserver<\/a><\/li>\n<li>update deployment checklist<\/li>\n<li>update deployment docs with a warning<\/li>\n<li>add tests for new flag<\/li>\n<li>update existing tests to include extended output<\/li>\n<\/ul>\n<p>I have started by updating the relevant tests to fail and added another to cover the new flag. Then proceeded to add code to ensure the tests pass. Finally I then tested the output in a local Django project which prompted me to switch the output to be coloured yellow as a warning.<\/p>\n<p>Next up is to go through each of the four documentation pages to update content or add new content. Hopefully this will be ready for review before the weekend. Fingers crossed!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-146","title":"My third Django ticket","summary":"Picking up the ticket I created","date_modified":"2024-07-31T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#djangonaut_space","#contributing","#open_source"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-145","content_html":"<p>In our final part of this short series on deploy Django, I am going to briefly walk through installing <a href=\"https:\/\/dokku.com\/\">dokku<\/a> and using it for a Django app.<\/p>\n<p>First up is to note that the documentation is easy to get along with in terms of installing the software.<\/p>\n<ol>\n<li>Install via package or <code>wget<\/code><\/li>\n<li>Setup SSH keys and global domains<\/li>\n<\/ol>\n<p>Next it is on to creating an application, this is the initial hostname and label your app gets throughout dokku as a reference. Dokku also has plugins to allow other services (eg databases, queues, notifications etc) to be used with an application.<\/p>\n<pre class=\"language-bash\"><code class=\"language-bash\">dokku apps:create my-django-project\n\n<span class=\"token comment\"># install plugins<\/span>\n<span class=\"token function\">sudo<\/span> dokku plugin:install https:\/\/github.com\/dokku\/dokku-postgres.git\n<span class=\"token function\">sudo<\/span> dokku plugin:install https:\/\/github.com\/dokku\/dokku-letsencrypt.git\n\ndokku postgres:create my-django-project-db\ndokku postgres:link my-django-project-db my-django-project\n<\/code><\/pre>\n<p>You are then ready to attempt a deployment. This is exactly like Heroku if you have used them. It is a git-based deployment, which means adding a new remote server and doing a git push to that remote.<\/p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">git<\/span> remote <span class=\"token function\">add<\/span> dokku dokku@dokku.me:my-django-project\n<span class=\"token function\">git<\/span> push dokku main\n<\/code><\/pre>\n<p>This will build the application and then push it live. The final steps are setting up SSL and dealing with some Django specifics.<\/p>\n<p>Enabling SSL:<\/p>\n<pre class=\"language-bash\"><code class=\"language-bash\">dokku letsencrypt:set --global email your-email@example.com\ndokku domains:set my-django-project my-project.example.com\ndokku letsencrypt:enable my-django-project\ndokku letsencrypt:cron-job --add\n<\/code><\/pre>\n<p>Finally static files can be handled by the whitenoise package and media files handled by the nginx instance that dokku uses itself. Rather than repeat myself, here is a <a href=\"https:\/\/ponytech.net\/blog\/django-deployment-dokku\">link to a blog post<\/a> I have followed myself.<\/p>\n<p>And with that you have a live Django project running on your own server with all the goodness of a git push experience.\nDoes this all feel a bit too complex? I&#x27;m happy to help either over a coffee chat or a longer project. Drop me a line and we can get your Django deployments sorted.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-145","title":"Deploying Django - Part 3","summary":"Using a self-hosted PaaS to host your Django project","date_modified":"2024-07-30T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#deployment","#VPS","#production","#dokku"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-144","content_html":"<p>Our last post dealt with deploying Django to a PaaS service. Today we will look at the other main option which is using a VPS or Linux server to host a Django project. This route does require more knowledge and familiarity with your Linux flavour of choice and the command line. The typical programs used are as follows:<\/p>\n<ul>\n<li>A WSGI or ASGI server (eg gunicorn, uWSGI, daphne, Uvicorn)<\/li>\n<li>A standard webserver (caddy, nginx, apache)<\/li>\n<li>Some way of running programs in the background (ie deamoning processes). This is either using the OS built-in system or supervisor is a good option.<\/li>\n<li>Also standard tools like bash &amp; ssh etc<\/li>\n<\/ul>\n<p>Again there are plenty of tutorials out on the internet that cover the precise steps to get this all up and running, therefore I will keep this to an overview of what each tool does.<\/p>\n<p>First up is the W\/ASGI server, this is the server that actually runs the Django project. You should not being using <code>runserver<\/code> here at all. W\/ASGI servers hook into <code>wsgi.py<\/code> or <code>asgi.py<\/code> in your project and can have multiple processes or threads to handle the expected load of a production setup. They will not handle static or media files.<\/p>\n<p>This is where the standard webserver comes into play. They can serve your static files, media files, proxy through to the WSGI server and provide some sane defaults to protect your server from the internet. Personally I default to nginx, but there are other options which are worth exploring.<\/p>\n<p>Finally the above programs needs to be managed in a way that they don&#x27;t stop when you disconnect from the server. Most linux systems provide <code>systemctl<\/code> which allows the running of programs as services or if you don&#x27;t have access then <code>supervisor<\/code> is an excellent choice. These can also be used to manage background workers or other programs like redis that you might end up using.<\/p>\n<p>The final element here is the actual deployment. All of the above is just creating a space for your code run correctly. Typically the deployment starts as a bash script which performs roughly the following steps:<\/p>\n<ol>\n<li>Pull new code from git<\/li>\n<li>Do any release commands (eg, collectstatic, migrate, other management commands or frontend build steps)<\/li>\n<li>Restart the WSGI server and any background workers<\/li>\n<\/ol>\n<p>This process would result in some minimal downtime and there are tools to help with this. There are also tools like <code>dokku<\/code> which provide a self-hosted PaaS solution which give the benefits of both worlds, the cheaper costs of a VPS with the dev experience of a PaaS. A tool like this is now my default go to choice for the simplicity and experience it provides.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-144","title":"Deploying Django - Part 2","summary":"Using a VPS to host your Django project","date_modified":"2024-07-29T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#deployment","#VPS","#production","#linux"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-143","content_html":"<p>Yesterday we introduced the two main methods of deploying a Django project. One of those options was to use a Platform as a Service (PaaS). These are typically more expensive in terms of dollars, but usually quicker to get from zero to deployed and normally have some help in maintaining your project over the long term. There are plenty of options out there and plenty of tutorials that can take you step by step to get everything up and running so I am not going to create duplicate content.<\/p>\n<p>Instead let&#x27;s focus on a few principles and gotchas that you will need to consider if going this route. The first is that you will need to also pay for a hosted database. When using a PaaS provider, the machine where you run your code will be consider ephemeral and this includes the file system. This means that using an sqlite DB will lose data on every restart of your project and these restarts are not in your control. The mitigation is to use a hosted database.<\/p>\n<p>Similarly this applies to static and media files as well. They cannot be stored on the same disk as the code. Typically you will want to opt for some object storage like S3 or Cloudflare&#x27;s R2 (django-storages can help with this). If you don&#x27;t have media files then you can also use the whitenoise package to host your static files in conjunction with a CDN.<\/p>\n<p>Finally most of these providers have free plans, but will require you to pay at some point and the premium in price comes at scale. This means knowing how scaling affects the costs of your project and whether you can afford to keep paying that premium. The alternative typically requires more time investment (which does have a monetary cost), but hosting costs are typically more stable and can be more resillent to cost increases when scaling. That alternative is hosting it yourself on a VPS or similar.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-143","title":"Deploying Django","summary":"Using a PaaS to host your Django project","date_modified":"2024-07-26T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#deployment","#PaaS","#production"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-142","content_html":"<p>The deployment of any web application can be broken down into two major steps:<\/p>\n<ol>\n<li>Creating a space or environment where the code will run.<\/li>\n<li>Having a process of getting the code from source control into that space and running it.<\/li>\n<\/ol>\n<p>Both of these steps can be manual or automatic, simple or complex and the while tooling has evolved over the last decade or so, these two core steps have remained the same.<\/p>\n<p>In terms of Django specifically, the first step normally involves one of these options:<\/p>\n<ol>\n<li>Setting up a virtual private server (or similar)<\/li>\n<li>Creating an account on a hosting PaaS<\/li>\n<\/ol>\n<p>Then the 2nd step either involves a bash script, Github Actions or other CI pipeline which then restarts a WSGI\/ASGI process which is running the Django project.<\/p>\n<p>As you can see the basic concept of deployments is fairly simple, it&#x27;s in the details where things get complicated.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-142","title":"Django deployments in a 100 words","summary":"Going from my laptop to the world","date_modified":"2024-07-25T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#deployment","#development","#production"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-141","content_html":"<p>Dear Junior Developer,<\/p>\n<p>You want to a job that pays the bills and more, but your stuck in the cycle of needing show your skills or experience when you have little or none to show, except perhaps school work. Know this, any employer hiring junior engineers should have the expectation that you will learn on the job and require mentorship in various areas. Know that one of your key skills ought to be a willingness to learn.<\/p>\n<p>That said, having a small portolio is an excellent idea. My advice to you is not to pad your resume with multiple projects that are clones of existing sites, eg a twitter clone or an e-commerce platform. While they do demonstrate skills and might get you an interview, it likely means you will not stand out at all from all the other candidates.<\/p>\n<p>Instead create a blog to learn the basics as well as documenting your journey and clarify how you think through writing. Next create a single project, this project should be on a subject that interests you and actively helps you solve a problem in your life. Add to it over time, make improvements as you get more skilled, extend it when you want to learn a new technology or try a new platform for hosting.<\/p>\n<p>This project will help far more than any clone projects in an interview for two main reasons. First, it&#x27;s likely that this project will standout on your resume far more than any clone project, my example of a project like this was getting robots to play the game of chicken. It intriuged the hiring managers that they wanted an interview in part just to ask me about that project. Which leads me to the second point, that interviews are conversations, when the hiring managers ask you about the project, speak about it, why you created it, what problem it solves, be passionate!<\/p>\n<p>This won&#x27;t guarantee that you will get hired, but it might just get you that one step closer. It certainly works with me when I have hired and when I hire again.<\/p>\n<p>A Senior Developer<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-141","title":"Dear Junior Developer...","summary":"Create a portfolio that will get you an interview","date_modified":"2024-07-24T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#dear_junior","#portfolio","#hiring","#interviews"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-140","content_html":"<p>This is another topic that comes up fairly regularly with those new to Django and new to database design, and to be honest I remember it taking me a while to get my head around it as well when I started.<\/p>\n<p>In database design, the one of the first rules you get taught is that every table needs an ID column (see <a href=\"\/100-words\/day-131\">day-131<\/a>), the typical thing that might happen is a developer then defining an <code>id<\/code> field on model with one of <code>CharField<\/code>, <code>IntegerField<\/code>, <code>PostiveIntegerField<\/code> or a <code>UUIDField<\/code>. Out of these options only <code>UUIDField<\/code> comes close to being valid.<\/p>\n<p>The above is what not to do, instead do the following. Django provides the <code>AutoField<\/code> and <code>BigAutoField<\/code> which enforces an auto-increment at the database level. Secondly Django will automatically add an <code>id<\/code> field if one isn&#x27;t defined on the model, therefore the correct option is to not include an <code>id<\/code> field.<\/p>\n<p>The second mistake is newer developers using <code>UUIDField<\/code> by default across their project. Arguments for this choice are usually about storage\/performance concerns or security vectors. Storage is cheap these days and for 90% of projects isn&#x27;t going to be a concern in terms of performance. When it comes to security vectors the simpler solution is to add a new field as an external ID which can be a unique <code>UUIDField<\/code> without being a primary key. See <a href=\"\/100-words\/day-34\">Day 34<\/a> for adding a field like this to an existing models. For new models you can add the following field:<\/p>\n<pre class=\"language-python\"><code class=\"language-python\">  uuid <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>UUIDField<span class=\"token punctuation\">(<\/span>default<span class=\"token operator\">=<\/span>uuid<span class=\"token punctuation\">.<\/span>uuid4<span class=\"token punctuation\">,<\/span> null<span class=\"token operator\">=<\/span><span class=\"token boolean\">False<\/span><span class=\"token punctuation\">,<\/span> editable<span class=\"token operator\">=<\/span><span class=\"token boolean\">False<\/span><span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>Simply put, use the sane defaults Django provides and concern yourself more with the larger data model!<\/p>\n<p>Disclaimer: The only times that this probably doesn&#x27;t this apply is when connecting Django to a existing legacy database or you are operating at a scale that means you have hired experts that know better. Although this is the 1% of projects out there and not the default.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-140","title":"Primary Keys in Django","summary":"By default use Django's default primary key","date_modified":"2024-07-23T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#primary_keys","#models","#databases"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-139","content_html":"<p>A few years back I created a small plugin for TailwindCSS. It originially started out, as most projects do, of a thought that starts with &quot;I wonder if I can do...&quot;.<\/p>\n<p>In this case it was expanding on the capabilities of the Tailwind config file. By default the file has most of the makings of <a href=\"https:\/\/uxdesign.cc\/design-tokens-for-dummies-8acebf010d71\">design tokens<\/a> done for you, and I wondered if you could natively build a design system declaritvely in the config file. Well it turns out you can, by abusing the <code>@apply<\/code> API. The result was my first (and at time of writing) only npm package.<\/p>\n<p>The Github repo is here: <a href=\"https:\/\/github.com\/softwarecrafts\/tailwind-component-classes\">https:\/\/github.com\/softwarecrafts\/tailwind-component-classes<\/a><\/p>\n<p>Essentially I took the idea of Tailwind constructing classes (especially spacing and colours) from nested object and colapsing them into single classes so a config file like this:<\/p>\n<pre class=\"language-js\"><code class=\"language-js\">module<span class=\"token punctuation\">.<\/span><span class=\"token property-access\">exports<\/span> <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">{<\/span>\n  <span class=\"token literal-property property\">theme<\/span><span class=\"token operator\">:<\/span> <span class=\"token punctuation\">{<\/span>\n    <span class=\"token literal-property property\">extend<\/span><span class=\"token operator\">:<\/span> <span class=\"token punctuation\">{<\/span>\n      <span class=\"token comment\">\/\/ ...<\/span>\n    <span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">,<\/span>\n  <span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">,<\/span>\n  <span class=\"token literal-property property\">components<\/span><span class=\"token operator\">:<\/span> <span class=\"token punctuation\">{<\/span>\n    <span class=\"token literal-property property\">btn<\/span><span class=\"token operator\">:<\/span> <span class=\"token punctuation\">{<\/span>\n      <span class=\"token literal-property property\">_<\/span><span class=\"token operator\">:<\/span> <span class=\"token string\">&#x27;shadow text-blue-900 border border-blue-400 px-4 py-2&#x27;<\/span><span class=\"token punctuation\">,<\/span>\n      <span class=\"token literal-property property\">error<\/span><span class=\"token operator\">:<\/span> <span class=\"token string\">&#x27;text-red-900 border-red-500&#x27;<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token literal-property property\">heading<\/span><span class=\"token operator\">:<\/span> <span class=\"token punctuation\">{<\/span>\n      <span class=\"token literal-property property\">_<\/span><span class=\"token operator\">:<\/span> <span class=\"token string\">&#x27;font-bold&#x27;<\/span><span class=\"token punctuation\">,<\/span>\n      <span class=\"token number\">1<\/span><span class=\"token operator\">:<\/span> <span class=\"token string\">&#x27;text-4xl&#x27;<\/span><span class=\"token punctuation\">,<\/span>\n      <span class=\"token number\">2<\/span><span class=\"token operator\">:<\/span> <span class=\"token string\">&#x27;text-3xl&#x27;<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token literal-property property\">container<\/span><span class=\"token operator\">:<\/span> <span class=\"token string\">&#x27;m-4&#x27;<\/span><span class=\"token punctuation\">,<\/span>\n  <span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">,<\/span>\n  <span class=\"token literal-property property\">plugins<\/span><span class=\"token operator\">:<\/span> <span class=\"token punctuation\">[<\/span><span class=\"token function\">require<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;tailwind-component-classes&#x27;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">,<\/span>\n<span class=\"token punctuation\">}<\/span>\n<\/code><\/pre>\n<p>generates the following css class names for you:<\/p>\n<pre><code>.container\n.heading\n.heading-1\n.heading-2\n.btn\n.btn-error\n<\/code><\/pre>\n<p>You can see this <a href=\"https:\/\/play.tailwindcss.com\/4KpGKazbeS\">example in action here<\/a><\/p>\n<p>The project needs some work for all the Tailwind V3 features, which are numerous and fairly undocumented for plugins last time I looked. It would also seem that this may become obsolete if the folks at Tailwind drop the config file entirely.<\/p>\n<p>It was a fun challenge and it has helped a few people from what I can tell.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-139","title":"Tailwind Component Classes","summary":"Reintroducing this little project","date_modified":"2024-07-22T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#tailwind","#plugins"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-138","content_html":"<p>The Django messages framework is not a real-time chat thing (see channels for that). It is very simply a way to send temporary messages to the user that then get clearer automatically once they have been displayed to a user. The messages are stored either in cookies or the session. They have a level like a log message (success, info, warning, error) and they can be styled using the <code>MESSAGE_TAGS<\/code> setting.<\/p>\n<p>To get started with messages is very simple. The default project has all the necessary settings already configured as it is used in the admin. It is just a matter of adding the following code to a base template which renders any messages<\/p>\n<pre class=\"language-html\"><code class=\"language-html\">{% if messages %}\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>ul<\/span> <span class=\"token attr-name\">class<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>messages<span class=\"token punctuation\">&quot;<\/span><\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    {% for message in messages %}\n    &lt;li{% if message.tags %} class=&quot;{{ message.tags }}&quot;{% endif %}&gt;{{ message }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>li<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    {% endfor %}\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>ul<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n{% endif %}\n<\/code><\/pre>\n<p>Then from a view using the following code:<\/p>\n<pre class=\"language-python\"><code class=\"language-python\">messages<span class=\"token punctuation\">.<\/span>debug<span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">,<\/span> <span class=\"token string\">&quot;%s SQL statements were executed.&quot;<\/span> <span class=\"token operator\">%<\/span> count<span class=\"token punctuation\">)<\/span>\nmessages<span class=\"token punctuation\">.<\/span>info<span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">,<\/span> <span class=\"token string\">&quot;Three credits remain in your account.&quot;<\/span><span class=\"token punctuation\">)<\/span>\nmessages<span class=\"token punctuation\">.<\/span>success<span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">,<\/span> <span class=\"token string\">&quot;Profile details updated.&quot;<\/span><span class=\"token punctuation\">)<\/span>\nmessages<span class=\"token punctuation\">.<\/span>warning<span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">,<\/span> <span class=\"token string\">&quot;Your account expires in three days.&quot;<\/span><span class=\"token punctuation\">)<\/span>\nmessages<span class=\"token punctuation\">.<\/span>error<span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">,<\/span> <span class=\"token string\">&quot;Document deleted.&quot;<\/span><span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>The final thing to note with messages is that styling them becomes very easy using the <code>MESSAGE_TAGS<\/code> setting like so (using Bootstrap class styling for alerts):<\/p>\n<pre class=\"language-python\"><code class=\"language-python\">MESSAGE_TAGS <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">{<\/span>\n    messages<span class=\"token punctuation\">.<\/span>DEBUG<span class=\"token punctuation\">:<\/span> <span class=\"token string\">&quot;alert alert-secondary&quot;<\/span><span class=\"token punctuation\">,<\/span>\n    messages<span class=\"token punctuation\">.<\/span>INFO<span class=\"token punctuation\">:<\/span> <span class=\"token string\">&quot;alert alert-info&quot;<\/span><span class=\"token punctuation\">,<\/span>\n    messages<span class=\"token punctuation\">.<\/span>SUCCESS<span class=\"token punctuation\">:<\/span> <span class=\"token string\">&quot;alert-success alert&quot;<\/span><span class=\"token punctuation\">,<\/span>\n    messages<span class=\"token punctuation\">.<\/span>WARNING<span class=\"token punctuation\">:<\/span> <span class=\"token string\">&quot;alert alert-warning&quot;<\/span><span class=\"token punctuation\">,<\/span>\n    messages<span class=\"token punctuation\">.<\/span>ERROR<span class=\"token punctuation\">:<\/span> <span class=\"token string\">&quot;alert alert-danger&quot;<\/span><span class=\"token punctuation\">,<\/span>\n<span class=\"token punctuation\">}<\/span>\n<\/code><\/pre>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-138","title":"Django messages in 100 words","summary":"This is not a chat system!","date_modified":"2024-07-19T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#messages","#toast"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-137","content_html":"<p>This week I have started working with a new client who has a NextJS app and a Chrome Extension and with Supabase as the backend. NextJS and Chrome Extensions are not new to me, as <a href=\"https:\/\/comfortmonitor.live\">comfortmonitor.live<\/a> uses the same stack except with AirTable as the backend.<\/p>\n<p>Supabase is the new things for me to wrap my head around, it&#x27;s essentially an API layer on top of Postgres. It&#x27;s nice by having some of the flows built out for a frontend heavy thing like a Chrome Extension which means no need for a backend server to maintain and in some ways very comparable to Django&#x27;s main selling points, with one major exception. The default starting point is the production hosted environment, not a local development environment. While Supabase does provide a local setup, it&#x27;s not the default which a key mistake here (IMO), especially when it comes to managing schema migrations.<\/p>\n<p>Perhaps I am just to used to Django&#x27;s migration system, but it took me far to long to realise that the previous developer of this new client hadn&#x27;t stored any of the migration changes to the database in the codebase, which leads to untracked changes occuring on the production system. This might be fine when starting out or a small side project, but this kind of thing gives me the chills when I discover it. Database migrations need to be tracked in source control.<\/p>\n<p>Luckily Supabase provides a way to generate the migrations from the production database and all is now well! Here&#x27;s to the next issue to solve for this client!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-137","title":"Local first development practices","summary":"Always try consider the next developer along","date_modified":"2024-07-18T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#development_practices","#local_dev"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-136","content_html":"<p>I am currently nearing the end of a recruitment process (which I will share about at a later date) and, as part of the process, I have been asked to write down a memo of &quot;How I like to work&quot;. This triggered a memory of the website <a href=\"https:\/\/www.manualof.me\/\">Manual of Me<\/a> which I had seen but not used. I have borrowed the concept and created my own Notion page which is available <a href=\"https:\/\/softwarecrafts.notion.site\/Manual-of-Me-Andrew-Miller-024fb634b2df4ccc9083ec916a1b51af\">here<\/a>.<\/p>\n<p>But what&#x27;s included in this manual, you ask? It&#x27;s essentially a list of questions for you to answer which allows other (potential) coworkers to get to know you. This would help in all environments, but espeically remote situations where body language can easily be missed. To complete one requires a large amount of introspection and knowning one&#x27;s self. How do you like to communicate, what are your best working hours for what, how do I like to recieve feedback, how do I deal with stress? These are just some of the questions and being honest it the best thing here since it will exclude those that you probably wouldn&#x27;t want to work with and draw those who do get you closer to you.<\/p>\n<p>Have you ever written a memo\/manual like this? How did it go? Let me know in the comments on social media or drop me an email, I would love to hear from you!<\/p>\n<p>PS Wish me luck that I get this done! I have struggled writing this manual today!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-136","title":"A Manual for Me","summary":"A framework for sharing about yourself","date_modified":"2024-07-17T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#reflection","#manual_of_me","#working_styles"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-135","content_html":"<p>Yesterday we explored the idea that complex interfaces ought to be simplified with a clear separation between read actions and write actions. This separation, I have come to realise, is used to great effect in some apps which prevents us from taking a more active role in what these apps or services are supposed to provide.<\/p>\n<p>The service in which I first had this realisation was through the bank apps I use. If the banking sector existed to serve it&#x27;s customers first, then I would propose it exists for us to actively manage our money which in turn would allow each person to grow their wealth through the monetary products they offer (eg savingss accounts). However pretty much every banking app and website is designed with a &#x27;read&#x27; perspective. You can easily read your balance and a history of transactions, but moving money around manually is hidden in a menu and moving money automatically hidden within that or doesn&#x27;t exist.<\/p>\n<p>Compare a banking app to a spreadsheet or word processor. The default interface invites you to create and edit documents to be an active participant with the software interface, not to simply read what has been presented to you.<\/p>\n<p>We as creators of software interfaces (no matter how well or badly it looks) need to be aware of these perceptions. They limit what the user thinks is possible even if the tools are all there for them to acheive their goal. This has happened to me with the Monzo bank app in the past, it took a real cognative effort to setup some sensible money management and it&#x27;s still not where I would like to be. We can (and should) be aware and do better.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-135","title":"Read and write interfaces - Part 2","summary":"How an interface can limit what you think can be done.","date_modified":"2024-07-16T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#interface_design","#read_vs_write","#ui","#design","#ui_design"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-134","content_html":"<p>On a daily basis we all interact with plenty of different interfaces to go about our world from those that are simple (eg a door <a href=\"https:\/\/en.wikipedia.org\/wiki\/The_Design_of_Everyday_Things\">or perhaps not<\/a>) to the complex (email clients or group chat apps) to the ridiculous (I&#x27;m looking at you Google, AWS and Salesforce).<\/p>\n<p>That said I one I have noticed with interfaces is that they get significantly more complex when an application tries to allow for reading information and writing information in the same place. Before we go further, let&#x27;s clarify two definitions:<\/p>\n<ul>\n<li>&#x27;Reading&#x27; in this context is anytime an application is displaying some data or information to the user for them to have a greater understanding of a situation. This could be CPU usage, Bank transactions or emails.<\/li>\n<li>&#x27;Writing&#x27; in the context is performing any action to something in the UI. This includes creating, editing or deleting things.<\/li>\n<\/ul>\n<p>The complex power applications mentioned above don&#x27;t do this too well in my opinion. For example I would love to see a cloud provider that only allows edits via an API or infrastructure code, which would allow the focus of the UI to be solely on providing insights to the running services and machines being used.<\/p>\n<p>However this separation can present an even deeper level of ingrained perception of how an application should behave. Tomorrow we will look at this with a focus on banking applications.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-134","title":"Read and write interfaces","summary":"Have a screen (or even the app) do one thing and one thing well","date_modified":"2024-07-15T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#interface_design","#read_vs_write","#ui","#design","#ui_design"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-133","content_html":"<p>So I am now half-way through Session 2 of Djangonaut space and it has so far been the boost I needed get involved in contributing to the Django codebase. A quick summary so far of what I have achieved:<\/p>\n<ul>\n<li>Closed off my first ticket and PR that is now live in the documentation<\/li>\n<li>Claimed a second ticket which has an open PR which has been reviewed and I need to make changes<\/li>\n<li>Made a proposal on the forum which I have converted to a ticket which has been accepted! It also got claimed by someone so I will look forward to seeing the PR when it comes.<\/li>\n<\/ul>\n<p>The Djangonaut Space discord is excellent and just the place to ask seamlingly silly questions about the contributing process or anything else as well as build tighter connections in the community. I would love to seem some of this spill over into the main server, but scaling community is a very difficult thing to do well.<\/p>\n<p>The other realisation is that I think I will take the time to play the role of a product owner, but revisiting some old tickets and summarising what actions are needed to get it over the line or even just identifying the next concrete step. While this isn&#x27;t writing code, it feels to me like one of the more impactful things to do in Trac.<\/p>\n<p>A final idea that has been knocking around my head is that Djangonaut graduates could be the perfect group of people to make a concerted effort on some of the larger features in Django that the community wants achieve. Almost if there was an advanced version of a session that got a team together to tackle a specific item on the roadmap (which was discussed earlier this year in an informal session). It would definitely have more of a drive to it and would likely go on longer than 8 weeks.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-133","title":"Djangonaut Space - Week 4","summary":"Reflections and updates from halfway through","date_modified":"2024-07-12T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#djangonaut_space"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-132","content_html":"<p>So let&#x27;s quick set the context for this example, we are a library with users that can borrow books from us. This might simply start as a list of books that have been borrowed or are currently being borrowed.<\/p>\n<pre><code>Book    | Borrower | Date Borrowed | Date Returned\n--------------------------------------------------\nA book  | A reader | 01\/01\/2024    | 21\/01\/2024\n<\/code><\/pre>\n<p>Simple, but it breaks the golden rule that the name of the books and borrowers gets repeated multiple times. Additionally this simple model means we cannot store extra information about a book (eg the author) or a borrower (eg email address)<\/p>\n<p>Therefore the correct data model for this very small example is as follows:<\/p>\n<pre><code>Book\n  - ID\n  - title\n  - author\n  - description\n  - page count\n\nBorrower\n  - ID\n  - name\n  - email address\n\nBorrowings\n  - ID\n  - Book ID\n  - Borrower ID\n  - Date Borrowed\n  - Date Returned\n<\/code><\/pre>\n<p>In the Borrowing entity above you can see we have swapped the title and names for ID references. This demonstrates the other two aspects yesterday, each item having a unique identifier (ID) which enables relations, ie using ID references in other entities.<\/p>\n<p>Hopefully this illustrates the beginnings of how to create a data model. A final note that it&#x27;s important to note that this thinking is easier to do before getting to code and also done with someone who is a subject matter expext on the domain at hand (In the example above a Librarian would be the best person to consult).<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-132","title":"Databases for Non Technical Folk - Part 2","summary":"An example of how the rules play out.","date_modified":"2024-07-11T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#databases","#relational","#data_modelling"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-131","content_html":"<p>When most technical people talk about building a database or refer to the database, they are often talking about a data model. A data model is a fairly abstract thing. Typically it&#x27;s a simplified version of the world that some software works with and a user interacts with via a webpage or mobile app. A data model could be implemented in Excel or similar and still be just as effective. That said a typcial relational database has a few more guard rails in place to help out.<\/p>\n<p>The golden rule of data models is that any one piece of information (eg an email, a date of a transaction, amount spent) has <em>one and only one place<\/em> where it should be stored. This means an email address should ideally be attached to only a single place in the whole model, this would likely be a table that describes a &#x27;user&#x27; in the system.<\/p>\n<p>There are two other key concepts in data models, first is that each instance of a entity (eg a User) needs to have a unique identifier. This then enables the second key concept of each individual entity can be related to another entity in the overall data model. Having relationships means that the golden rule above can hold true.<\/p>\n<p>Having trouble understanding the above? Tomorrow, I will step through a short example of the above.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-131","title":"Databases for Non Technical Folk","summary":"They sound complex, but they are very similar to Excel","date_modified":"2024-07-10T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#databases","#relational","#data_modelling"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-130","content_html":"<p>Yesterday I introduced Django templates in 100 words and in it mentioned template tags. Template tags are simple functions that take an input from the template and return some processed output. They need to live in a package called <code>templatetags<\/code> within an app or project.<\/p>\n<p>As with most areas of Django, it is possible to create your own tags to perform logic not available in directly in Django. Creating your own template tag is fairly simple but can become complex if required. A simple example from the docs:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token comment\"># templatetags\/time.py<\/span>\n<span class=\"token keyword\">import<\/span> datetime\n<span class=\"token keyword\">from<\/span> django <span class=\"token keyword\">import<\/span> template\n\nregister <span class=\"token operator\">=<\/span> template<span class=\"token punctuation\">.<\/span>Library<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n\n\n<span class=\"token decorator annotation punctuation\">@register<span class=\"token punctuation\">.<\/span>simple_tag<\/span>\n<span class=\"token keyword\">def<\/span> <span class=\"token function\">current_time<\/span><span class=\"token punctuation\">(<\/span>format_string<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    <span class=\"token keyword\">return<\/span> datetime<span class=\"token punctuation\">.<\/span>datetime<span class=\"token punctuation\">.<\/span>now<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">.<\/span>strftime<span class=\"token punctuation\">(<\/span>format_string<span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>Which would be used as follows in a template:<\/p>\n<pre class=\"language-html\"><code class=\"language-html\">{% load time %}\n\n{% current_time %}\n<\/code><\/pre>\n<p>Am important reminder that templates and hence template tags should be reserved for presentational logic only. Therefore I stongly recommend not starting new database queries inside a template tag function, this will very likely lead to hidden performance issues such as N+1 queries. The same applies to template filters as well.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-130","title":"Django Templatetags in a 100 words","summary":"Extending the logic possible in templates","date_modified":"2024-07-09T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#templates","#template_tags"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-129","content_html":"<p>Django Templates are the &#x27;view&#x27; aspect of an MVC architecture or termed MTV in Django. This layer is all about presenting data to the user for them to view it, typically in a web browser. Templates are mostly HTML with extra tags and filters to render context data that has been passed to a template via a view or context processor. The output of a template is a HTML string that get&#x27;s sent back to the browser to render as a web page.<\/p>\n<p>The &#x27;template tags&#x27; have if statements for conditional rendering, loops for lists or tabular data and structural blocks which mean we as developers do not need to repeat ourselves for common elements that do not change between different pages (eg head tags, nav bar, menus etc).<\/p>\n<p>Currently this is a much as Django gets involved in the frontend side of the web, which isn&#x27;t much at all. That said templates are worth learning and while intentionally limited, this is a benefit and they are still hugely powerful.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-129","title":"Django Templates in a 100 words","summary":"Rendering HTML with data","date_modified":"2024-07-08T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#templates"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-128","content_html":"<p>Something completely different to round off the week. Today I hosted my second ever &quot;meeting with myself&quot;. This is a new monthly practice which involves the following:<\/p>\n<ol>\n<li>Go to a cafe (preferably one with nice food!)<\/li>\n<li>Take only a notebook &amp; pen, importantly no laptop.<\/li>\n<li>Reflect on life for a couple of hours, listen to God. For me areas of life are currently:\na. Business\nb. Family\nc. Marriage\nd. Parenting\nc. Health\ne. Friendships<\/li>\n<\/ol>\n<p>The key things here is the length of time, this is not something to rush. Take the time to people watch if you like or get mildly distracted by the world around you. The second is to minimise the distractions of the digital world, for me, that&#x27;s my laptop which hosts work, numerous open tabs to read and emails to answer.<\/p>\n<p>So far I have found it to be an excellent way to slow down and enjoy a bit of life, especially when I find it easy to be depressed about a day or lack the excitment to get going!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-128","title":"A Meeting with myself: a monthly reflection","summary":"A new practice for my mental wellbeing","image":"https:\/\/softwarecrafts.co.uk\/photos\/day-128-cafe.jpg","date_modified":"2024-07-05T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#self_reflection","#growth"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-127","content_html":"<p>So I am almost at the end of week 3 of Djangonaut space. This has been a busy week for it! At the end of last week I picked up a docs ticket which got merged in early this week!<\/p>\n<p>Achievement unlocked, first contribution to Django done!<\/p>\n<p>Additionally I have gone back to the first ticket I picked up and created a new smaller <a href=\"https:\/\/github.com\/django\/django\/pull\/18332\">PR<\/a> which allows for further customisations to messages beyond what the ticket specifies. This has been reviewed and I need to work on it further, before it can be merged. Mainly I have missed documentation updates and new tests.<\/p>\n<p>Finally I have drafted a new proposal on the <a href=\"https:\/\/forum.djangoproject.com\/t\/proposal-borrow-warning-from-werkzeug-for-runserver\/32668\">forum<\/a> which is add a warning that runserver is not suitable for production. The discussion is positive so far, that said do add your opinion either way. This is a second attempt at my earlier proposal to rename <code>runserver<\/code> to <code>devserver<\/code> but this time it&#x27;s baby steps! This is also part of my goal to see the entire contribution process from idea to merge.<\/p>\n<p>More updates next week!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-127","title":"Djangonaut Space: Week 3","summary":"My weekly progress update","date_modified":"2024-07-04T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#djangonaut_space"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-126","content_html":"<p>Today we cover the final aspect of customising the Django Admin. This is adding your own urls and views to the admin and as such uses the last two days topics, overriding templates and methods on the ModelAdmin or AdminSite class.<\/p>\n<p>This will follow the same pattern as any normal Django view but with some minor changes. First up we need to define our urls, this is done by overriding <a href=\"https:\/\/docs.djangoproject.com\/en\/5.0\/ref\/contrib\/admin\/#django.contrib.admin.ModelAdmin.get_urls\">get_urls<\/a> on the ModelAdmin or AdminSite class.<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token comment\"># from the django docs:<\/span>\n<span class=\"token keyword\">class<\/span> <span class=\"token class-name\">MyModelAdmin<\/span><span class=\"token punctuation\">(<\/span>admin<span class=\"token punctuation\">.<\/span>ModelAdmin<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">get_urls<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        urls <span class=\"token operator\">=<\/span> <span class=\"token builtin\">super<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">.<\/span>get_urls<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n        my_urls <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">[<\/span>path<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;my_view\/&quot;<\/span><span class=\"token punctuation\">,<\/span> self<span class=\"token punctuation\">.<\/span>admin_site<span class=\"token punctuation\">.<\/span>admin_view<span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">.<\/span>my_view<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">]<\/span>\n        <span class=\"token keyword\">return<\/span> my_urls <span class=\"token operator\">+<\/span> urls\n<\/code><\/pre>\n<p>Next we need to create our view, this is done as a method on the class so we can access the other attributes and methods of the ModelAdmin class. Again from the docs:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token keyword\">class<\/span> <span class=\"token class-name\">MyModelAdmin<\/span><span class=\"token punctuation\">(<\/span>admin<span class=\"token punctuation\">.<\/span>ModelAdmin<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    <span class=\"token comment\"># ...<\/span>\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">my_view<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">,<\/span> request<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        <span class=\"token comment\"># ...<\/span>\n        context <span class=\"token operator\">=<\/span> <span class=\"token builtin\">dict<\/span><span class=\"token punctuation\">(<\/span>\n            <span class=\"token comment\"># Include common variables for rendering the admin template.<\/span>\n            self<span class=\"token punctuation\">.<\/span>admin_site<span class=\"token punctuation\">.<\/span>each_context<span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token comment\"># Anything else you want in the context...<\/span>\n            key<span class=\"token operator\">=<\/span>value<span class=\"token punctuation\">,<\/span>\n        <span class=\"token punctuation\">)<\/span>\n        <span class=\"token keyword\">return<\/span> TemplateResponse<span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">,<\/span> <span class=\"token string\">&quot;sometemplate.html&quot;<\/span><span class=\"token punctuation\">,<\/span> context<span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>Finally we need to create a fresh template for our content. The only thing is to extend from an admin template so you get the look and feel of the template and the prebuilt header, breadcrumbs etc. Here is an example extending from the <code>base_site.html<\/code><\/p>\n<pre class=\"language-html\"><code class=\"language-html\">{% extends &quot;admin\/base_site.html&quot; %}\n{% block content %}\n...\n{% endblock %}\n<\/code><\/pre>\n<p>And that&#x27;s it! This technique is great for one off views, but I don&#x27;t recommend creating extensive custom views as this will clutter your admin classes. Finally to reiterate, this can apply to the <code>AdminSite<\/code> class if you have a view which doesn&#x27;t rely on one particular model.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-126","title":"Customising the Django Admin - Part 6","summary":"Creating your own admin views","date_modified":"2024-07-03T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#django_admin","#customisation","#views"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-125","content_html":"<p>The ModelAdmin and AdminSite classes contain a number of methods available for more complex customisations when the simple attribute options don&#x27;t cut it.<\/p>\n<p>The majority of these methods are hooks to customise the options and take the form of <code>get_&lt;attribute&gt;<\/code>. This allows conditional customisation on a per object or per request basis. Otherwise these also allow for creating a base Admin class to ensure certain fields are always present in every admin.<\/p>\n<p>The other methods are permission based or deal with customising the saving and deletion of models. The final set of methods allow for the addition of new views or customisation of existing views, we will cover this in more detail tomorrow.<\/p>\n<p>Reference: <a href=\"https:\/\/docs.djangoproject.com\/en\/5.0\/ref\/contrib\/admin\/#modeladmin-methods\">https:\/\/docs.djangoproject.com\/en\/5.0\/ref\/contrib\/admin\/#modeladmin-methods<\/a><\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-125","title":"Customising the Django Admin - Part 5","summary":"Using the methods available for more complex customisations","date_modified":"2024-07-02T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#django_admin","#customisation"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-124","content_html":"<p>The next level of customising the Django admin is overriding the available templates. The level of customisation on this can range from tweaking the colours to redoing the layout for existing pages.<\/p>\n<p>To start, find the relevant template <a href=\"https:\/\/github.com\/django\/django\/tree\/main\/django\/contrib\/admin\/templates\/admin\">here<\/a> and copy it&#x27;s location into your code base ensuring to match the directory struture. Then it&#x27;s just a matter of editing away!<\/p>\n<p>The one thing to be careful with, is understanding all the places a template is used as most of them are generic enough to be used across multiple models.<\/p>\n<p>At this point, it is worth mention that there are plenty of packages that extend the admin so be sure to research if any meet your needs.<\/p>\n<ul>\n<li><a href=\"https:\/\/djangopackages.org\/grids\/g\/admin-interface\/\">Admin packages<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/originalankur\/awesome-django-admin?tab=readme-ov-file\">Awesome Django Admin<\/a><\/li>\n<\/ul>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-124","title":"Customising the Django Admin - Part 4","summary":"Changing the look & feel of the admin","date_modified":"2024-07-01T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#django_admin","#customisation","#templates"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-123","content_html":"<p>The next stage of customisation to the admin still uses the options mentioned yesterday, but today we will a couple of the more complex options.<\/p>\n<p>First up is <code>actions<\/code>. This list of function references adds items to a dropdown on the list page, allowing for simple custom workflows or editing multiple rows.<\/p>\n<p>Next is knowing that quite a few of the options yesterday also allow you to reference not just fields, but properties that exist on the model or on the admin class itself. This allows for more complex values to be represented easily.<\/p>\n<p>Finally for today there is the <code>form<\/code> option. This allows you to define your own <code>ModelForm<\/code> for the admin (which what Django is auto-generating anyway). This level of customisation means you have full control over the form fields, widgets and validation like any other form in your project.<\/p>\n<p>Tomorrow we will look at the methods available on the ModelAdmin class<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-123","title":"Customising the Django Admin - Part 3","summary":"Taking action and being more dynamic","date_modified":"2024-06-28T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#django_admin","#customisation"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-122","content_html":"<p>Before you start customising the admin ensure that every model has a <code>__str__<\/code> method defined and that easily idenfitied the model if for example this string appeared in a dropdown menu. This means ideally you need avoid using a relation in this method.<\/p>\n<p>The default way to customise the admin is to create a class that inherits from the <code>ModelAdmin<\/code> class, for example:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token decorator annotation punctuation\">@admin<span class=\"token punctuation\">.<\/span>register<\/span><span class=\"token punctuation\">(<\/span>MyModel<span class=\"token punctuation\">)<\/span>\n<span class=\"token keyword\">class<\/span> <span class=\"token class-name\">MyModelAdmin<\/span><span class=\"token punctuation\">(<\/span>admin<span class=\"token punctuation\">.<\/span>ModelAdmin<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    <span class=\"token keyword\">pass<\/span>\n<\/code><\/pre>\n<p>This currently doesn&#x27;t yet do anything special. By default the first options to add are <code>list_display<\/code>, <code>list_filter<\/code> and <code>search_fields<\/code>. These cover the list page by adding columns to the table, filters on the right and a search bar at the top.<\/p>\n<p>Next let&#x27;s consider the detail (add\/change) page. Here the most common options I reach for are <code>readonly_fields<\/code>, <code>raw_id_fields<\/code> and <code>filter_horizontal<\/code>. Read only is for when you don&#x27;t want a field to be edited, raw_id_fields is a typical performance improvement when dealing with a Foreign Key which by default renders a drop down with all of the values of the relation. Finally <code>filter_horizontal<\/code> makes an Many To Many field clearer to use.<\/p>\n<p>The final two options worth mentioning today are <code>fieldsets<\/code> and <code>inlines<\/code>. <code>fieldsets<\/code> allow grouping and customisation of the form on the detail page and <code>inline<\/code> allow specifying AdminInlines which uses Django formsets to create related models on the details.<\/p>\n<p>The full list of options available can be <a href=\"https:\/\/docs.djangoproject.com\/en\/5.0\/ref\/contrib\/admin\/#modeladmin-options\">found on this page<\/a>. I recommend taking the time be familiar with the options as they can take you very far in creating a useful admin interface.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-122","title":"Customising the Django Admin - Part 2","summary":"Using the available options and attributes","date_modified":"2024-06-27T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#django_admin","#customisation"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-121","content_html":"<p>The Django admin is one of the major selling points of Django. Out of the box you get a fully-featured web based GUI to edit your database and it&#x27;s free! Granted it&#x27;s not the prettiest looking thing today, but free!<\/p>\n<p>This means there is often the question of customisation, something along the lines of how do show X in the admin or how can the admin do Y? Today let&#x27;s summarise the options available, they are (from simple to more complex):<\/p>\n<ol>\n<li>Using the provided attributes on the ModelAdmin class or AdminSite class.\na. simple attribute eg <code>list_display<\/code>\nb. complex attrubutes eg <code>form<\/code> or <code>actions<\/code><\/li>\n<li>Using template inheritance to customise the HTML of the admin<\/li>\n<li>Overriding the methods on an ModelAdmin class<\/li>\n<li>Adding extra urls &amp; views to an Admin class (ModelAdmin or AdminSite)<\/li>\n<\/ol>\n<p>Over the next few days we will go through this list and give some examples of how to do each of them. A final point, I expect the usage of these techniques to decrease as we go through the list.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-121","title":"Customising the Django Admin","summary":"What's available and what's reasonable to customise","date_modified":"2024-06-26T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#django_admin","#customisation"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-120","content_html":"<p>I spent some of my time today browsing Trac while I wait if my <a href=\"https:\/\/forum.djangoproject.com\/t\/ticket-13376-messages-should-have-an-expire-flag\/32190\">forum post<\/a> gains traction to see if I could find any other tickets that grabbed my attention. I was narrowing my focus to look at bugs or optimisations with the hopes that they they wouldn&#x27;t require any consensus on the forum.<\/p>\n<p>Currently I have five potential tickets open (see below), but my feeling today when reading them was one of intimidation! There was lots of technical details of areas that I know how to use, but to change? that&#x27;s a whole new ball game. I am going to give myself a day or so, then I will be picking one of the tickets below and getting started.<\/p>\n<ul>\n<li><a href=\"https:\/\/code.djangoproject.com\/ticket\/23790\">https:\/\/code.djangoproject.com\/ticket\/23790<\/a><\/li>\n<li><a href=\"https:\/\/code.djangoproject.com\/ticket\/27489\">https:\/\/code.djangoproject.com\/ticket\/27489<\/a><\/li>\n<li><a href=\"https:\/\/code.djangoproject.com\/ticket\/26756\">https:\/\/code.djangoproject.com\/ticket\/26756<\/a><\/li>\n<li><a href=\"https:\/\/code.djangoproject.com\/ticket\/22724\">https:\/\/code.djangoproject.com\/ticket\/22724<\/a><\/li>\n<li><a href=\"https:\/\/code.djangoproject.com\/ticket\/25281\">https:\/\/code.djangoproject.com\/ticket\/25281<\/a><\/li>\n<\/ul>\n<p>Got any opinions on which I should pick? Drop me a message and let me know! (No promises though!)<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-120","title":"Week 2 of Djangonaut Space","summary":"Waiting and moving on","date_modified":"2024-06-25T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#djangonaut_space"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-119","content_html":"<p>Last week I nailed the most complex query optimisation I have worked on in my career (to date).<\/p>\n<p>First let&#x27;s set the context. We have a table which includes an inline dropdown to quickly add\/remove users to a group. The initial template code was something like this:<\/p>\n<pre class=\"language-html\"><code class=\"language-html\">\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>table<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n{% for client in clients %}\n    ...\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    ...\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>td<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>select<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n                <span class=\"token comment\">&lt;!-- One new database query here --&gt;<\/span>\n                {% for group in user.get_groups %}\n                    <span class=\"token comment\">&lt;!-- Another new database query here --&gt;<\/span>\n                    {% if user in group.users.all %}\n                        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>option<\/span> <span class=\"token attr-name\">selected<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{group}}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>option<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n                    {% else %}\n                        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>option<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{group}}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>option<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n                    {% endif %}\n                {% endfor %}\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>select<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>td<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    ...\n{% endfor %}\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>table<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n<\/code><\/pre>\n<p>As you can see the for loop triggers one set of N+1 queries, but for each of those, there is another query which probably gets us to NM+1 queries!<\/p>\n<p>The first step here is work out what data we need to get from the database. This is the name of the group (title attribute) and whether a client is a member of the group.<\/p>\n<p>Since we have a list of groups to annotate to each client we can use Django&#x27;s postgres specific function <code>ArraySubquery<\/code> which allows this. Next we need to use a <code>JSONObject<\/code> to allow us to annotate an object with multiple attributes.<\/p>\n<p>Currently this leaves our query looking like this:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\">  clients <span class=\"token operator\">=<\/span> clients<span class=\"token punctuation\">.<\/span>annotate<span class=\"token punctuation\">(<\/span>\n      usergroups<span class=\"token operator\">=<\/span>ArraySubquery<span class=\"token punctuation\">(<\/span>\n          UserGroup<span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span>annotate<span class=\"token punctuation\">(<\/span>\n              groups<span class=\"token operator\">=<\/span>JSONObject<span class=\"token punctuation\">(<\/span>\n                  title<span class=\"token operator\">=<\/span>F<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;title&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n                  is_member<span class=\"token operator\">=<\/span>????<span class=\"token punctuation\">,<\/span>\n              <span class=\"token punctuation\">)<\/span>\n          <span class=\"token punctuation\">)<\/span>\n      <span class=\"token punctuation\">)<\/span>\n  <span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>The real crux of this optimisation was the <code>is_member<\/code> attribute of the <code>JSONObject<\/code>, I stuggled to work out the specific syntax for this and ended up with duplicate entires. However with the help of the community in Discord I got to the solution which is an <code>Exists<\/code> query on the through table between <code>User<\/code> and <code>UserGroup<\/code> which resulted in the following annotation:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\">  clients <span class=\"token operator\">=<\/span> clients<span class=\"token punctuation\">.<\/span>annotate<span class=\"token punctuation\">(<\/span>\n      usergroups<span class=\"token operator\">=<\/span>ArraySubquery<span class=\"token punctuation\">(<\/span>\n          UserGroup<span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span>annotate<span class=\"token punctuation\">(<\/span>clientid<span class=\"token operator\">=<\/span>OuterRef<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;pk&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">.<\/span>annotate<span class=\"token punctuation\">(<\/span>\n              groups<span class=\"token operator\">=<\/span>JSONObject<span class=\"token punctuation\">(<\/span>\n                  title<span class=\"token operator\">=<\/span>F<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;title&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n                  is_member<span class=\"token operator\">=<\/span>Exists<span class=\"token punctuation\">(<\/span>\n                      UserGroup<span class=\"token punctuation\">.<\/span>clients<span class=\"token punctuation\">.<\/span>through<span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span><span class=\"token builtin\">filter<\/span><span class=\"token punctuation\">(<\/span>\n                          usergroup_id<span class=\"token operator\">=<\/span>OuterRef<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;pk&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span> user_id<span class=\"token operator\">=<\/span>OuterRef<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;clientid&quot;<\/span><span class=\"token punctuation\">)<\/span>\n                      <span class=\"token punctuation\">)<\/span>\n                  <span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n              <span class=\"token punctuation\">)<\/span>\n          <span class=\"token punctuation\">)<\/span>\n      <span class=\"token punctuation\">)<\/span>\n  <span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>The key aspect of this query is the first annotation which takes the OuterRef of the first Subquery to allow it be used in the inner subquery. Finally this allows the template to be simplified as follows:<\/p>\n<pre class=\"language-html\"><code class=\"language-html\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>table<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n{% for client in clients %}\n    ...\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    ...\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>td<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>select<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n                {% for group in client.usergroups %}\n                    {% if group.is_member %}\n                        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>option<\/span> <span class=\"token attr-name\">selected<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{group}}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>option<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n                    {% else %}\n                        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>option<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>{{group.title}}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>option<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n                    {% endif %}\n                {% endfor %}\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>select<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>td<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>tr<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    ...\n{% endfor %}\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>table<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n<\/code><\/pre>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-119","title":"Nested Subquerys in Django","summary":"Improving the performance of a complex page and removing multiple N+1 issues","date_modified":"2024-06-24T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#ORM","#subqueries"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-118","content_html":"<p>So yesterday I detailed how I picked <a href=\"https:\/\/code.djangoproject.com\/ticket\/13376\">ticket #13376<\/a>. Today is my progress so far.<\/p>\n<p>The ticket had already had some work done and several patch files were attached to the ticket. So to begin with I started by downloading the file, creating a new branch in my local copy of Django and trying to apply it. This took a little bit of work, but eventually the following git command worked for the most part<\/p>\n<pre><code>cd django\ngit apply --reject --whitespace=fix ..\/messages_expirity_capabilities.4.diff\n<\/code><\/pre>\n<p>This applied what it could and any conflicts got dumped in reject files. I then went through these files and applies the rest of the changes manually.<\/p>\n<p>After this I set about running the existing test suite to fix any failures of which there were a fair few! Once these were fixed I then submitted a <a href=\"https:\/\/github.com\/django\/django\/pull\/18286\">draft PR here<\/a>.<\/p>\n<p>However since this is a new feature and a non-trival change I opened a <a href=\"https:\/\/forum.djangoproject.com\/t\/ticket-13376-messages-should-have-an-expire-flag\/32190\">topic on the forum<\/a> to reestablish a consensus from the community. The discussion has started well, with the consideration of spec&#x27;ing the API and considering pushing this to a third-party package to stablise the API before merging it back into Django. I would welcome comments here so we can establish whether the ticket is something to continue pursuing.<\/p>\n<p>Currently I am going to mostly pause on this ticket until more comments have been made on the Forum.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-118","title":"Starting Ticket 13376","summary":"Starting on my first ticket.","date_modified":"2024-06-21T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#djangonaut_space","#contribution","#work_notes","#django_messages"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-117","content_html":"<p>Yesterday was an introduction into me joining Djangonaut space, today let&#x27;s dig into the details!<\/p>\n<p>First was picking that first ticket out of the current ~900! To do this I followed David Smith&#x27;s <a href=\"https:\/\/smithdc.uk\/blog\/2023\/how_to_find_a_ticket_on_django_trac\/\">blog post<\/a> on narrowing the Trac search by component and started browsing. While doing this two other thoughts were going through my head. First was something I have heard Carlton mention multiple times about picking a ticket. It goes something along the lines of you become the world expert of that ticket by the time you get into the details of a problem and have a nose around. Second is that I want to be in this for the long haul and contributing to Django be a thing I just do.<\/p>\n<p>Therefore I broadern these thought that I would like to pick a component or two, then focus on them. This resulted in me identifying the following components:<\/p>\n<ul>\n<li>Auth<\/li>\n<li>Messages<\/li>\n<li>Sites<\/li>\n<li>Documentation<\/li>\n<li>Generic Views<\/li>\n<\/ul>\n<p>Some of these components don&#x27;t have many open tickets which I like, although they are old and haven&#x27;t been touched, but I like the idea of clearing up some older tickets.<\/p>\n<p>Auth &amp; Documentation have more tickets, but I feel like could make contributions over the long haul. With auth I feel like Django core needs to catch up to remain true to it&#x27;s values of being batteries included and secure. Documentation matches my desire of helping others in Discord and writing here.<\/p>\n<p>However Messages won out since it has a grand total of 1 open ticket and I am a completionist at heart (video game geek here!) so being able to close this one out in some way would feel great!<\/p>\n<p>Tomorrow I will detail my progress on the ticket so far!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-117","title":"Picking up my first Django Ticket","summary":"Getting from 900 to 1.","date_modified":"2024-06-20T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#djangonaut_space","#contribution","#work_notes"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-116","content_html":"<p>So a I am slightly late on announcing this, but better late than never! I have been lucky enough to have been selected to be a Djangonaut for Session 2 of <a href=\"https:\/\/djangonaut.space\/\">Djangonaut Space<\/a>! I have joined the Mars team which has the focus of the Django project itself.<\/p>\n<p>I am hugely excited by this opportunity and the next 8 weeks here are likely going to represent my progress with tickets and the program in general.<\/p>\n<p>To begin with I have decided to pick up <a href=\"https:\/\/code.djangoproject.com\/ticket\/13376\">this ticket<\/a> which is the current last open ticket in <code>messages<\/code> of Django. It&#x27;s 13 years old and not a non-trivial change, as a result I have asked for new opinions on <a href=\"https:\/\/forum.djangoproject.com\/t\/ticket-13376-messages-should-have-an-expire-flag\/32190\">the forum<\/a>, please do add a comment if you have one!<\/p>\n<p>I have started by applying the existing patch from the ticket and fixing the tests.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-116","title":"Take off with Djangonaut Space!","summary":"Session 2 started this week","date_modified":"2024-06-19T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#djangonaut_space","#mentoring"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-115","content_html":"<p>Today we have one final post on business logic in Django. I have had a few responses over the last week to some of my suggestions so here is a shout out to those comments.<\/p>\n<p>First up is a comment from <a href=\"https:\/\/www.linkedin.com\/feed\/update\/urn:li:activity:7206679639556009986?commentUrn=urn%3Ali%3Acomment%3A%28activity%3A7206679639556009986%2C7206689853504159746%29&amp;dashCommentUrn=urn%3Ali%3Afsd_comment%3A%287206689853504159746%2Curn%3Ali%3Aactivity%3A7206679639556009986%29\">Maxim Danilov<\/a> on Linkedin. They mention the use of Mixins to hold business logic instead of Models directly based on my comments from <a href=\"\/100-words\/day-111\">Day 111<\/a>. Mixins are an excellent use case here assuming you have the same logic and interface across different models. This is also an excellent way to keep logic out of Models where they ideally are restricted to modelling the database.<\/p>\n<p>Another <a href=\"https:\/\/indiehackers.social\/@ghickman@mastodon.social\/112631736934962926\">comment<\/a> came from George Hickman who pointed me to a <a href=\"https:\/\/simoncrowe.hashnode.dev\/django-service-layers-beyond-fat-models-vs-enterprise-patterns\">recent rebuttal&#x27;s<\/a> of James Bennett&#x27;s no service layer&#x27;s article from yesterday. While I don&#x27;t agree with everything mentioned here, one standout quote from Simon is as follows:<\/p>\n<blockquote>\n<p>However, I think it&#x27;s a possible consequence of not giving busy engineers, who may be in a hurry to ship something, an easy answer to &quot;where do I put this?&quot; that doesn&#x27;t involve Django classes. <a href=\"https:\/\/simoncrowe.hashnode.dev\/django-service-layers-beyond-fat-models-vs-enterprise-patterns\">https:\/\/simoncrowe.hashnode.dev\/django-service-layers-beyond-fat-models-vs-enterprise-patterns<\/a><\/p>\n<\/blockquote>\n<p>This outlines the core premise of this short series, Django does dictate a lot of things to a developer as part of it&#x27;s batteries included mantra, but it doesn&#x27;t dictate everything. Other modules, classes or functions have always existed in every codebase I have interacted with that hold non Django specific code. Formalising this into a service layer is a practicality that exists today.<\/p>\n<p>As I round out this series, I have the following questions floating in my head as areas to explore:<\/p>\n<ul>\n<li>Could a custom manager dealing with multiple models be attached to multiple models to create a service-like layer using a Django class?<\/li>\n<li>In a related fashion what would it look like if Django had made the creation of custom Managers a requirement or more practically what would a codebase look like that enforced custom managers for each model?<\/li>\n<\/ul>\n<p>Finally some further architecture guides from across the Djangosphere:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/HackSoftware\/Django-Styleguide\">Hacksoft Style Guide<\/a><\/li>\n<li><a href=\"https:\/\/phalt.github.io\/django-api-domains\/\">Django API Domains<\/a><\/li>\n<li><a href=\"https:\/\/forum.djangoproject.com\/t\/where-to-put-business-logic-in-django\/282\/\">Forum Discussion<\/a><\/li>\n<\/ul>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-115","title":"Business Logic in Django: Bonus!","summary":"Some responses from the community","date_modified":"2024-06-18T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#business_logic"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-114","content_html":"<p>The final option in the list of where to store your business logic to create a Service Layer. A service layer is a module that provides an abstraction that hides the implementation details of which model or models are queried to the layers above (typically the view). For example<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token keyword\">from<\/span> tickets <span class=\"token keyword\">import<\/span> service\nmy_tickets <span class=\"token operator\">=<\/span> service<span class=\"token punctuation\">.<\/span>get_list<span class=\"token punctuation\">(<\/span>user<span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>This option generally isn&#x27;t popular in the Django community, for the broad general reasons. First YAGNI (You ain&#x27;t going to need it), a service layer serves to provide an absraction to swap out the data layer, in a Django project this is highly unlikely to happen as the ORM is one of the major reasons to pick Django.\nSecond, you end up reimplementing most of if not all of the QuerySet API which is given to you for free by Django.<\/p>\n<p>For a longer opinion on this check out James Bennett&#x27;s articles <a href=\"https:\/\/www.b-list.org\/weblog\/2020\/mar\/16\/no-service\/\">here<\/a>, and thanks to him for some of the reasoning included by myself.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-114","title":"Service Layers for Logic","summary":"The final option that mostly ignores Django's existing classes","date_modified":"2024-06-17T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#business_logic","#service_layer"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-113","content_html":"<p>Forms, Serializers (DRF or otherwise) or FilterSets also provide a decent place for business logic to live. It&#x27;s not perfect as these classes already exist to perform a specific purpose which could be abused if we add too much in one place. Namely Forms and Serializers are for transforming and validating data, FilterSets query data.<\/p>\n<p>What is common about all three class types is that they typically sit in between the view and model layers and typically perform an action that requires logic. It&#x27;s also the place where N classes exist relative to a single model class to fit different logical circumstances. Care must be taken not to overload a single Form\/Serializer class with too much logic, but also not to have a proliferation of classes that they become unmanageable.<\/p>\n<p>Currently this approach tends to be my personal go to solution for organising business logic. Next we cover the last in the original list: Service layers.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-113","title":"Forms, Serializers and FilterSets for Logic","summary":"Other options using existing components of Django","date_modified":"2024-06-14T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#business_logic","#forms","#serializers","#filtersets"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-112","content_html":"<p>Yesterday we considers Models as the place for business logic as it avoids duplication, but the reality is the abstraction isn&#x27;t quite right for every case. This leads us to custom Managers &amp; Querysets, where we can add our own methods to perform the necessary logic and can be called from most places in a project.<\/p>\n<p>Both are very similar abstractions for working the database via the Django ORM. As I understand it the main practical difference between the two is that QuerySets methods can be chained and Managers cannot (since they return QuerySets). Below is an example of this. <a href=\"https:\/\/fly.io\/django-beats\/organizing-database-queries-managers-vs-querysets\/\">Thanks to Mariusz for this example<\/a><\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token comment\"># Manager<\/span>\n<span class=\"token operator\">&gt;&gt;<\/span><span class=\"token operator\">&gt;<\/span> Person<span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span>experienced<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">.<\/span>with_extra_fields<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n<span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span>\nAttributeError<span class=\"token punctuation\">:<\/span> <span class=\"token string\">&#x27;QuerySet&#x27;<\/span> <span class=\"token builtin\">object<\/span> has no attribute <span class=\"token string\">&#x27;with_extra_fields&#x27;<\/span>\n\n<span class=\"token comment\"># QueySet<\/span>\n<span class=\"token operator\">&gt;&gt;<\/span><span class=\"token operator\">&gt;<\/span> Person<span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span>experienced<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">.<\/span>with_extra_fields<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">.<\/span>get<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">.<\/span>full_name\n<span class=\"token string\">&#x27;Joe Doe&#x27;<\/span>\n<span class=\"token operator\">&gt;&gt;<\/span><span class=\"token operator\">&gt;<\/span> Person<span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span>experienced<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">.<\/span>number_of_unique_names<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n<span class=\"token number\">1<\/span>\n<span class=\"token operator\">&gt;&gt;<\/span><span class=\"token operator\">&gt;<\/span> Person<span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span>number_of_unique_names<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n<span class=\"token number\">2<\/span>\n<\/code><\/pre>\n<p>Managers &amp; QuerySets have a defined API which allows for returning multiple instances or a single instance, but they don&#x27;t allow for working with a single instance as an input, unless you pass it in as a parameter. This breaks the chain as illustrated above though. One solution is to define similar functions on both a QuerySet and the Model, which does work, but does lead to some duplication which may even be harder to maintain in the long run.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-112","title":"Managers and Querysets for business logic","summary":"One step removed from Models","date_modified":"2024-06-13T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#business_logic","#models","#managers_and_querysets"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-111","content_html":"<p>When someone has been working with a Django for a while, the phrase &quot;Fat Models, thin Views&quot; tends to get used as a better way to organise the code.\nThe general thinking behind this phrase is that the database, represented as a Model in code, will be the source of truth for some data. Therefore logic related to that data ought to live as close as possible to this data.<\/p>\n<p>This leads to a reduction in duplication when a view will now call a method on a model. However this isn&#x27;t perfect, namely the Model class typically represents interacting with a single instance of the class, not multiple objects and dealing with multiple different models can easily get messy with code on a single Model.<\/p>\n<p>To deal with the first problem mentioned we will look at Managers &amp; QuerySets tomorrow.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-111","title":"Moving logic into Models","summary":"The next best advice that is given","date_modified":"2024-06-12T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#business_logic","#models"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-110","content_html":"<p>The first place that business logic normally lives is in Django&#x27;s views. Views are the default place to starting putting any custom code that you need. For example:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token keyword\">def<\/span> <span class=\"token function\">my_view<\/span><span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    <span class=\"token comment\"># some custom logic here<\/span>\n    <span class=\"token comment\"># ...<\/span>\n    <span class=\"token keyword\">return<\/span> render<span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">,<\/span> <span class=\"token string\">&#x27;my_template.html&#x27;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token punctuation\">{<\/span><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>Views generally keep code nice and simple to understand, but you inevitably end up repeating the same code across different views. This is fine for small projects or where you want to keep things really simple. However this is the main downside of having all your logic in your views.<\/p>\n<p>The initial solution to this would be to create plain python functions or classes to hold this shared code and they typically end up being held in one of the other methods we will discuss later in this series.<\/p>\n<p>Tomorrow we will look at Models as the container for business logic.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-110","title":"Business Logic inside Django Views","summary":"The default place to start","date_modified":"2024-06-11T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#business_logic","#views"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-109","content_html":"<p>A quick definition of business logic to begin with: This is the code that makes your project unique, it is the rules, biases and assumptions you have modelled in your code to make your web application work the way it does.<\/p>\n<p>However there is never a predefined place for this logic to live. In Django there are a few potential locations which I will cover over the next week. These locations are as follows:<\/p>\n<ul>\n<li>Views<\/li>\n<li>Models<\/li>\n<li>Managers\/Querysets<\/li>\n<li>Forms\/Serializers<\/li>\n<li>FilterSets<\/li>\n<li>Service Layer<\/li>\n<\/ul>\n<p>Each of these locations are entirely valid (well except perhaps the last one) depending on the size and scope of the project as well as the expertise of the developer. Tomorrow we will start with the simplest, Views<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-109","title":"Business Logic in Django","summary":"Where should it live?","date_modified":"2024-06-10T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#business_logic","#maintenance"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-108","content_html":"<p>Django has two types of migrations, they are schema and data migrations. Most will be familiar with schema migrations as these are the migrations that Django will auto generate for you using the command <code>makemigrations<\/code>.<\/p>\n<p>Data migrations on the other hand need to be written by hand. Typical use cases for data migrations are:<\/p>\n<ul>\n<li>One-off loading of data into the database (either from a file or directly in code)<\/li>\n<li><a href=\"\/100-words\/day-34\">Backfilling data<\/a> into a new field<\/li>\n<li>Moving data from table to a new field or new table<\/li>\n<\/ul>\n<p>Creating a data migration is fairly simple:<\/p>\n<ol>\n<li>Run <code>manage.py makemigrations &lt;my_app&gt; --empty --name &lt;what_the_migration_does&gt;<\/code><\/li>\n<li>Create a forward migration function with the following signature <code>my_forward_function(apps, schema_editor)<\/code><\/li>\n<li>Write the migration! Important Rule here, never import your models directly from your models.py, but using the <code>apps<\/code> parameter as follows: <code>apps.get_model(&#x27;apps.MyModel&#x27;)<\/code><\/li>\n<li>Write a reverse migration, this will unapply your migration when running the migrate command backwards (eg <code>manage.py migrate &lt;my_app&gt; zero<\/code>). If you cannot think of an appropriate reverse migration then you can use the noop function which is referenced as <code>migrations.RunPython.noop<\/code><\/li>\n<li>Finally add a <code>RunPython<\/code> operation in the operations list that specfies the forward and reverse migration.<\/li>\n<\/ol>\n<p>Overall you will have a python file that looks like this. This migration auto creates some Group instances that relates to another model<\/p>\n<pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">from<\/span> django<span class=\"token punctuation\">.<\/span>db <span class=\"token keyword\">import<\/span> migrations\n\n<span class=\"token keyword\">def<\/span> <span class=\"token function\">forward_migration<\/span><span class=\"token punctuation\">(<\/span>apps<span class=\"token punctuation\">,<\/span> schema_editor<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    Group <span class=\"token operator\">=<\/span> apps<span class=\"token punctuation\">.<\/span>get_model<span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;auth.Group&#x27;<\/span><span class=\"token punctuation\">)<\/span>\n    MyModel <span class=\"token operator\">=<\/span> apps<span class=\"token punctuation\">.<\/span>get_model<span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;MyApp.MyModel&#x27;<\/span><span class=\"token punctuation\">)<\/span>\n    <span class=\"token keyword\">for<\/span> model <span class=\"token keyword\">in<\/span> MyModel<span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span><span class=\"token builtin\">filter<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        group <span class=\"token operator\">=<\/span> Group<span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span>create<span class=\"token punctuation\">(<\/span>name<span class=\"token operator\">=<\/span><span class=\"token string-interpolation\"><span class=\"token string\">f&#x27;my_model_group_<\/span><span class=\"token interpolation\"><span class=\"token punctuation\">{<\/span>model<span class=\"token punctuation\">.<\/span>pk<span class=\"token punctuation\">}<\/span><\/span><span class=\"token string\">&#x27;<\/span><\/span><span class=\"token punctuation\">)<\/span>\n        model<span class=\"token punctuation\">.<\/span>recipe_group <span class=\"token operator\">=<\/span> group\n        model<span class=\"token punctuation\">.<\/span>save<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n\n<span class=\"token keyword\">def<\/span> <span class=\"token function\">reverse_migration<\/span><span class=\"token punctuation\">(<\/span>apps<span class=\"token punctuation\">,<\/span> schema_editor<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    Group <span class=\"token operator\">=<\/span> apps<span class=\"token punctuation\">.<\/span>get_model<span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;auth.Group&#x27;<\/span><span class=\"token punctuation\">)<\/span>\n    Group<span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span><span class=\"token builtin\">filter<\/span><span class=\"token punctuation\">(<\/span>name__startswith<span class=\"token operator\">=<\/span><span class=\"token string\">&#x27;my_model_group_&#x27;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">.<\/span>delete<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n\n<span class=\"token keyword\">class<\/span> <span class=\"token class-name\">Migration<\/span><span class=\"token punctuation\">(<\/span>migrations<span class=\"token punctuation\">.<\/span>Migration<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n\n    dependencies <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">[<\/span>\n        <span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;MyApp&#x27;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token string\">&#x27;0001_initial&#x27;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token punctuation\">]<\/span>\n\n    operations <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">[<\/span>\n        migrations<span class=\"token punctuation\">.<\/span>RunPython<span class=\"token punctuation\">(<\/span>forward_migration<span class=\"token punctuation\">,<\/span> reverse_migration<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token punctuation\">]<\/span>\n<\/code><\/pre>\n<p>And that&#x27;s it! The migration code obviously can be more complex, but this is the basics. More advanced options such as app dependencies, order and atomic options can be found in the <a href=\"https:\/\/docs.djangoproject.com\/en\/5.0\/topics\/migrations\/#data-migrations\">docs<\/a>.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-108","title":"Data Migrations in Django","summary":"What they are and how to make one","date_modified":"2024-06-07T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#migrations","#data_migrations"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-107","content_html":"<p>I have written one view about this already on <a href=\"\/100-words\/day-63\">day 63<\/a> which takes a more philosophical about learning technogies. All that said however, when building a website with any amount of functionality, Javascript will feature somewhere. But the real question is what&#x27;s the best Javascript thing to use with Django?<\/p>\n<p>The answer is a decision tree (which I may create another day) of sorts. I personally start with a lot of questions, here are a selection:<\/p>\n<ul>\n<li>Are you using Django templates or is the backend an API or is it both?<\/li>\n<li>Do you have an existing team of frontend developers or plans for one?<\/li>\n<li>Do you need to support multiple frontend environments (eg web, mobile, desktop, etc)<\/li>\n<li>Are you still learning Django?<\/li>\n<li>Do you have a particular technology you want to learn for this project?<\/li>\n<li>Is this project for fun or for business?<\/li>\n<li>Are you working in a team or working solo?<\/li>\n<li>Is this an existing project or a fresh start?<\/li>\n<\/ul>\n<p>Answers to those questions ought to reveal the best approach for a project. For example, working solo or in a small team that just requires a web presence, then I would start with just some Vanilla Javascript or small libraries like HTMX, AlpineJS or StimulusJS.<\/p>\n<p>If you have a team of frontend devs, or you are supporting multiple frontend environments then an investment into a heavier framework such as React, Vue or Angular is more likely to worth the effort. When it comes to multiple platforms then you might even be considering different languages such as Rust and the Tauri framework which in turn can use the heavier JavaScript frameworks.<\/p>\n<p>Finally if you are working with an existing codebase then the easiest thing is to follow existing conventions and only introduce new technologies in an incremental manner so either development doesn&#x27;t grind to a halt or the performance is affected.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-107","title":"Which Javascript framework should I use with Django?","summary":"Vanilla through to a Reactive Vue Sundae","date_modified":"2024-06-06T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#frontend","#javascript"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-106","content_html":"<p>The first required quote from Sir Tony Hoare on this topic is as follows:<\/p>\n<blockquote>\n<p>&quot;premature optimization is the root of all evil.&quot;<\/p>\n<\/blockquote>\n<p>Plenty of times I have have seen someone worry about optimisation of a page before it has even hit production is worrying. That isn&#x27;t to say you shouldn&#x27;t avoid obvious N+1 issues or similar known problems. But I do want to emphasis that a shipped non-optiminal page is better than page never shipped.<\/p>\n<p>This leads me to my second point. You need to be able measure performance before you can know what is slow and what is fast. My recommendations are using <a href=\"https:\/\/django-debug-toolbar.readthedocs.io\/en\/latest\/\">Django Debug Toolbar<\/a> for debugging performance locally and something like <a href=\"https:\/\/sentry.io\/\">Sentry<\/a>, (Scout)[<a href=\"https:\/\/www.scoutapm.com\/\">https:\/\/www.scoutapm.com\/<\/a>] or <a href=\"https:\/\/github.com\/jazzband\/django-silk\">django-silk<\/a> are excellent for hosted production environments. They all will highlight timings and various details about where code is slow and causing your users pain.<\/p>\n<p>Finally there is the actual optimisation work. Here the solution is most often the classic &#x27;It Depends&#x27;. That said, in my recent travels through a client project which was (and still is) riddled with N+1 issues, here are some common places to check for optimisations:<\/p>\n<ul>\n<li>Iterating over a queryset in a view and adding items to a python list<\/li>\n<li>Having custom template tags with queries in them, if they are called inside a template <code>{% for %}<\/code> loop tag<\/li>\n<li>Nested for loops in a view or in a template<\/li>\n<li>Calling model methods inside a for loop in the template (this often hides a query or three)<\/li>\n<\/ul>\n<p>I find the general solution here is to annotate the initial queryset with the required data so to flatten out any queries into a larger initial query. It won&#x27;t work all the time but it&#x27;s a starting point.<\/p>\n<p>Finally other possible optimisations are background workers like celery, to use async views, or JS libraries like HTMX to progressive load a page.<\/p>\n<p>I am sure I have missed something obvious so give me a shout on socials!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-106","title":"Improving the performance of your Django project","summary":"My personal overview of dealing with slowness","date_modified":"2024-06-05T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#performance","#optimisation"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-105","content_html":"<p>Context processors are functions, typically quite simple, that add data to the context every time you render a template. The best example of a context processor is the one that adds the user object to the context. This one is enabled by default inside Django, but it saves you having to remember to add the following code to every view.<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token keyword\">def<\/span> <span class=\"token function\">my_view<\/span><span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    <span class=\"token keyword\">return<\/span> render<span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;index.html&#x27;<\/span>\n        <span class=\"token punctuation\">{<\/span>\n            <span class=\"token comment\"># THIS Line<\/span>\n            <span class=\"token string\">&#x27;user&#x27;<\/span><span class=\"token punctuation\">:<\/span> request<span class=\"token punctuation\">.<\/span>user\n        <span class=\"token punctuation\">}<\/span>\n    <span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>and instead you have this single function:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token keyword\">def<\/span> <span class=\"token function\">my_context_processor<\/span><span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    <span class=\"token keyword\">return<\/span> <span class=\"token punctuation\">{<\/span>\n        <span class=\"token string\">&#x27;user&#x27;<\/span><span class=\"token punctuation\">:<\/span> request<span class=\"token punctuation\">.<\/span>user\n    <span class=\"token punctuation\">}<\/span>\n<\/code><\/pre>\n<p>Having too many context processors would add undue load to the rendering process as they affect every view so do take caution when adding them.<\/p>\n<p>Commone use cases for context processors are passing settings and feature flags.<\/p>\n<p>Finally the quick how to, step 1 write your function and return a dictionary. Step 2 add the python dotted path into the TEMPLATES settings with the other context processors. Done!<\/p>\n<p>A day later edit: Bapiste pointed out that I got it slightly wrong. Context processors are only used when the RequestConext is used, which is by default with the <code>render<\/code> shortcut function which was introduced in Django 1.3. See <a href=\"https:\/\/indiehackers.social\/@bmispelon@mastodon.social\/112560490018907291\">here for the discussion<\/a><\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-105","title":"Context Processors in 100 words","summary":"What they are and when you might want one","date_modified":"2024-06-04T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#context_processors"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-104","content_html":"<p>This post is another testament to how much you get for free with Django. On a fairly regular basis I see developers using a tool like pgAdmin or a GUI tool to inspect their database. My general recommendation is to <strong>not<\/strong> use those tools by default.<\/p>\n<p>Here&#x27;s why:<\/p>\n<ol>\n<li>They can allow a developer to edit the schema or otherwise modify the database (eg delete tables) which then creates havoc for the Django migrations system.<\/li>\n<li>Django providers <code>manage.py shell<\/code> to interact with the database via the ORM. The benefits here are that you have a full Python interpreter and you practice using the ORM functionality which can then be easily copy\/pasted to your code.<\/li>\n<li>Django also provides <code>manage.py dbshell<\/code> to drop into the likes of <code>psql<\/code> directly. Again this is better than a GUI as you are working through Django which means it will be available on any project in any environment (including a locked down production environment), no special permissions or exceptions required.<\/li>\n<li>Finally if you are using these tools to visualise your database then the django-extensions graph_models command or something like <a href=\"https:\/\/github.com\/pikhovkin\/django-schema-viewer\">django-schema-viewer<\/a> has you covered.<\/li>\n<\/ol>\n<p>Essentially learn to work with the tooling Django provides by default or via third-party packages before reaching for external tooling.<\/p>\n<p>Disclaimer: This doesn&#x27;t mean these external tools aren&#x27;t useful, but they have a place and that place shouldn&#x27;t be in a default setup.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-104","title":"Don't use pgAdmin (or similar tools)","summary":"Know what Django provides before reaching for extra tools","date_modified":"2024-06-03T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#tooling","#management_commands"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-103","content_html":"<p>When starting a new app, one of the requirements is to create an <code>apps.py<\/code> file with a class that inherits from the <code>AppConfig<\/code> class. Typically this has very minimal contents with just the <code>name<\/code> and <code>label<\/code> attributes set to a value. Additionally the default auto field might be set as well.<\/p>\n<p>What you might not know is that you can have multiple AppConfig classes that could do different things. The template-partials library has a good example of this <a href=\"https:\/\/github.com\/carltongibson\/django-template-partials\/blob\/main\/src\/template_partials\/apps.py\">here<\/a>. Carlton has setup two classes, one which does some auto configuration (&quot;magic&quot;) and another without. I have been pondering this week as I write about apps what else having multiple classes could be used for, below are some ideas.<\/p>\n<p>First up is having AppConfigs which let the user of a reusable app specify the primary key type they would like to use.<\/p>\n<pre class=\"language-py\"><code class=\"language-py\">\n<span class=\"token keyword\">class<\/span> <span class=\"token class-name\">AutoAppConfig<\/span><span class=\"token punctuation\">(<\/span>AppConfig<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    name <span class=\"token operator\">=<\/span> <span class=\"token string\">&#x27;myapp&#x27;<\/span>\n    default_auto_field <span class=\"token operator\">=<\/span> <span class=\"token string\">&#x27;django.db.models.AutoField&#x27;<\/span>\n    default <span class=\"token operator\">=<\/span> <span class=\"token boolean\">True<\/span>\n\n<span class=\"token keyword\">class<\/span> <span class=\"token class-name\">UUIDAppConfig<\/span><span class=\"token punctuation\">(<\/span>AppConfig<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    name <span class=\"token operator\">=<\/span> <span class=\"token string\">&#x27;myapp&#x27;<\/span>\n    default_auto_field <span class=\"token operator\">=<\/span> <span class=\"token string\">&#x27;django.db.models.UUIDField&#x27;<\/span>\n<\/code><\/pre>\n<p>Another typical use is to register signals in the <code>ready<\/code> method. Perhaps one use case could be to opt-in\/out of these signals. This might be useful for a CI or Test environment or if there was a more explicit way of calling the relevant code. This could be expanded to load a different AppConfig per environment and therefore containing some settings or logic to within an app over adding settings to the main project settings or performing checks in this system on startup (although this might be better with the checks framework!)<\/p>\n<p>Do you have any thoughts about what else the AppConfig class could be used for? or even having multiple classes?<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-103","title":"Django apps - Using AppConfigs","summary":"What can we use them for?","date_modified":"2024-05-31T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#django_apps","#app_configs"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-102","content_html":"<p>Today&#x27;s post will cover where the app name gets embedded into a project. Essentially the app provides a namespace for certain items, namely the python modules themselves and the Django models. Knowing this is key if you want to move a model or requiring some serious refactoring.<\/p>\n<p>The first obvious places that it crops up is in <code>INSTALLED_APPS<\/code> and other settings (eg <code>AUTH_USER_MODEL<\/code>), next is python import statements (<code>from my_app.models import MyModel<\/code>).<\/p>\n<p>The slightly less obvious places are migration files where app names are used in the dependency list. Additionally the app names get injected into the database in 2 main ways, first is the ContentType model (if that app is enabled) and also in the default database table name for any model, which is <code>myapp_mymodel<\/code> this is a key part of namespacing so that models from separate apps do not clash.<\/p>\n<p>Tomorrow we will round off talking about apps about the customisations that could be done customising AppConfigs or having multiple versions.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-102","title":"Django apps - what are the implications","summary":"Where does the app name get used?","date_modified":"2024-05-30T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#django_apps"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-101","content_html":"<p>Yesterday we considered a single app. Today it is multiple apps. The approach here is to list different parts of the project as separate apps in the <code>INSTALLED_APPS<\/code> setting.<\/p>\n<p>The main question when it comes to multiple apps is what criteria should be used to create a new app? When a single app has gotten too big? or covering too many concerns? There is no single right answer here and it it down to the architecture of the overall project. My personal stance is that each app should be a logical unit of functionality, that could potentially be moved out of the Django project if required. I tend to think of apps as an abstraction to enable thinking or grocking the codebase at a higher level of thought, especially when it comes to dependencies. A well architected Django project at the &#x27;app&#x27; level should have a single directional dependency graph.<\/p>\n<p>The other main benefit is the separation of concerns when working on a codebase, the migration dependencies will be more complex, but this allows for more changes at a single point in time. Two developers can work on separate apps without a concern for clashes in the dependency graph of the migrations.<\/p>\n<p>The obvious drawback is the increased complexity in the project structure and moving models between apps is doable but a not trivial task. You need to know what you are doing. This can happen when the logical dependencies are not thought about upfront, but are often thought about when the codebase is a mess.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-101","title":"Django apps - using multiple apps","summary":"Ten apps to bind them... (well maybe not 10 precisely)","date_modified":"2024-05-29T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#django_apps","#multiple_apps"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-100","content_html":"<p>A single app leverages python&#x27;s native modules over anything that Django adds on top. Here you might call the app something like <code>core<\/code> and you would start with a single file for each aspect of the app (eg models.py, views.py etc). Then as the app expands you would refactor these into a directory with an <code>__init__.py<\/code> and separate files as required. For example:<\/p>\n<pre><code>- models\/\n  - __init__.py\n  - user_profiles.py\n  - order.py\n- views\/\n  - __init__.py\n<\/code><\/pre>\n<p>Having a single app does keep the project layout simple and clean. The key is ensuring your files don&#x27;t get overwhelming in size and breaking them down as they grow in size. One issue you might run into is managing migration files since you ideally want a linear history, but you will more likely run into migration branches\/conflicts during development.<\/p>\n<p>Tomorrow we will look at multiple apps.<\/p>\n<p>Also a small pat on the back as this marks 100 blog posts this year!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-100","title":"Django apps - a single app","summary":"One app to rule them all","date_modified":"2024-05-28T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#django_apps","#single_app"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-99","content_html":"<p>Django apps are another topic that firstly confuse newcomers somewhat and secondly the community has split opinions on.<\/p>\n<p>The main existance for Django apps (as I understand it) is to enable reusable apps. This allows us as developers to distribute packages for common pieces of functionality such as authentication, content management, ratings etc.<\/p>\n<p>However since the tutorial teaches us to create an app to start the project, then the question typically pops us of &#x27;how many apps should I have?&#x27; and depending on who you ask they will either say 1 or many. Both have there advantages and consequences which we can dive into tomorrow.<\/p>\n<p>Personally I am multiple app person, but what about you? Drop me a commwnt and your biggest reason why.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-99","title":"Django apps - how many do you need?","summary":"one verus many in a project","date_modified":"2024-05-27T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#django_apps"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-98","content_html":"<p>Yesterday I mentioned contributing financially, but what other ways can you contribute?<\/p>\n<p>One easy way is helping others, primarily on Discord or the Forum, but it&#x27;s not limited to just these two places. Answering questions on Stack Overflow is equally valid.<\/p>\n<p>Volunteering at conferences, events or hosting your own local meetup raises the profile of Django and demostrates your involvement. Then there is speaking at conferences, attending sprints (either in-person or online) or producing content such as blogs, podcasts or videos.<\/p>\n<p>Finally Django (or more specifically the DSF) needs contributors at the board level and in working groups. The majority of these roles do <em>not<\/em> need software engineers, but specialists in there areas of fundraising or social media to name a couple. I am sure there are other areas that could do with help as well!<\/p>\n<p>This is just a small list of items that I know about but the list is endless! My main point is here is that we probably need more non-code contributions over code contributions. What do you think? Let me know!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-98","title":"Other Django contributions","summary":"Other that code and time, what else is there","date_modified":"2024-05-24T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#contributions"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-97","content_html":"<p>I have written about this on Linkedin before, but time is due to write about it here as well. When the topic of giving back comes up most developers think about giving their time and skills to fix a bug or create a package. While these endeavours are noble, I find them hard to do given the limited time I have available between family, work, health and sleep!<\/p>\n<p>Therefore last year, inspired by the guys at Foxley Talent, I started donating 5% of my reveune to the Django Software Foundation (DSF). It&#x27;s an relatively easy thing to do since 5% will always be in line with the money I have available compared with a fixed amount.<\/p>\n<p>I also as a freelancer\/consultant like to communicate this value to my clients by having it as a line item on the invoice for them to see where some of their money is going.<\/p>\n<p>I do wonder what would happen if the DSF recieved 5% of the profits or reveune from other companies that used Django. What would the DSF be able to achieve for Django? Could you commit to a percentage amount? or better yet asking your employer? I recommend you seriously consider it if Django has impacted your career in a meaningful way.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-97","title":"Giving back to Django","summary":"Giving time is hard, donating money is easy","date_modified":"2024-05-23T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#dsf","#donations","#time_vs_money"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-96","content_html":"<p>This is a classic beginner question that gets asked at some point after completing the tutorial. Sadly there is no right answer to this question and the community is fairly evenly split from what I can tell in it&#x27;s preferences.<\/p>\n<p>Personally I am a fan of class-based views and I typically reach for FormView or TemplateView from the generic classes to further reduce amount of boilerplate I need to write. That said they do confuse newer devs who don&#x27;t yet know the method names or attributes required to achieve what they would like.<\/p>\n<p>Function-based views are generally easier to understand what is going on, it&#x27;s clear a request comes in and then gets processed.<\/p>\n<p>The key with both variations is minimise the complexity (or at least be happy with the complexity you choose). Function-based views can get unwieldly if they get too long and refactored functions are not placed in a logical file. Class-based views do benefit from namespacing of helper functions, but the complexity can creep in with Mixins and the inheritance hierarchy.<\/p>\n<p>You can mix them in a project, do not feel chained to one or the other! My final advice is to start with function-based views, then try class-based views once you feel like it!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-96","title":"Functions or Classes for Views","summary":"Which to use?","date_modified":"2024-05-22T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#views"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-95","content_html":"<p>Today I was adding a cache busting button to a simple custom admin page I had created and I wanted to leverage the Django messages framework to show whether the cache busting had worked. The page was already using HTMX to load a table depending on a dropdown selection so I thought <a href=\"https:\/\/htmx.org\/examples\/update-other-content\/#oob\">HTMX&#x27;s Out Of Band swap<\/a> would be perfect here.<\/p>\n<p>At this point I knew I was going to returning multiple partial templates in a single response so low and below before I knew it I was installing Carlton&#x27;s <a href=\"https:\/\/github.com\/carltongibson\/django-template-partials\/\">template-partials<\/a> package to make this easier!<\/p>\n<p>Once I got my head around how it works I was able to setup the following template code to render messages sent in a HTMX partial response (note this is extending an admin template).<\/p>\n<pre class=\"language-html\"><code class=\"language-html\">{% partialdef messages %}\n\t{% if messages %}\n\t<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>ul<\/span> <span class=\"token attr-name\">hx-swap-oob<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>outerHTML:.messagelist<span class=\"token punctuation\">&quot;<\/span><\/span> <span class=\"token attr-name\">class<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>messagelist<span class=\"token punctuation\">&quot;<\/span><\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n\t\t{% for message in messages %}\n\t\t&lt;li{% if message.tags %} class=&quot;{{ message.tags }}&quot;{% endif %}&gt;{{ message.message|capfirst }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>li<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n\t\t{% endfor %}\n\t<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>ul<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n\t{% endif %}\n{% endpartialdef %}\n\n{% block messages %}\n\t<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>ul<\/span> <span class=\"token attr-name\">class<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>messagelist<span class=\"token punctuation\">&quot;<\/span><\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n\t{% partial messages %}\n\t<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>ul<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n{% endblock messages %}\n<\/code><\/pre>\n<p>Along with the following python in the view (although this could easily be a middleware to apply across every request):<\/p>\n<pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">def<\/span> <span class=\"token function\">my_view<\/span><span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">)<\/span>\n    messages<span class=\"token punctuation\">.<\/span>success<span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">,<\/span> <span class=\"token string\">&#x27;Cache Busted!&#x27;<\/span><span class=\"token punctuation\">)<\/span>\n    existing_messages <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">[<\/span>\n        <span class=\"token punctuation\">{<\/span><span class=\"token string\">&quot;message&quot;<\/span><span class=\"token punctuation\">:<\/span> message<span class=\"token punctuation\">.<\/span>message<span class=\"token punctuation\">,<\/span> <span class=\"token string\">&quot;tags&quot;<\/span><span class=\"token punctuation\">:<\/span> message<span class=\"token punctuation\">.<\/span>tags<span class=\"token punctuation\">}<\/span>\n        <span class=\"token keyword\">for<\/span> message <span class=\"token keyword\">in<\/span> messages<span class=\"token punctuation\">.<\/span>get_messages<span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">)<\/span>\n    <span class=\"token punctuation\">]<\/span>\n\n    response_content <span class=\"token operator\">+=<\/span> render_to_string<span class=\"token punctuation\">(<\/span>\n        <span class=\"token string-interpolation\"><span class=\"token string\">f&quot;<\/span><span class=\"token interpolation\"><span class=\"token punctuation\">{<\/span>template_name<span class=\"token punctuation\">}<\/span><\/span><span class=\"token string\">#messages&quot;<\/span><\/span><span class=\"token punctuation\">,<\/span>\n        <span class=\"token punctuation\">{<\/span><span class=\"token string\">&quot;messages&quot;<\/span><span class=\"token punctuation\">:<\/span> existing_messages<span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>Note that the messages content in the view needs to added after the primary content for the partial template. Also this following HTMX config needs to be set so HTMX knows there are multiple fragments:<\/p>\n<pre class=\"language-js\"><code class=\"language-js\">htmx<span class=\"token punctuation\">.<\/span><span class=\"token property-access\">config<\/span><span class=\"token punctuation\">.<\/span><span class=\"token property-access\">useTemplateFragments<\/span> <span class=\"token operator\">=<\/span> <span class=\"token boolean\">true<\/span>\n<\/code><\/pre>\n<p>This would be awesome to be a default for the admin, but that means likely adopting HTMX or replicating the above in JQuery, so seems unlikely. I may see if this can be wrapped into a small package though.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-95","title":"Exploring HTMX, messages and template partials","summary":"Getting Django messages without a reload","date_modified":"2024-05-21T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#django_messages","#template_partials"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-94","content_html":"<p>Once you have a project up and running, the next step is likely hitting a problem you don&#x27;t know how to solve, which means asking for help.<\/p>\n<p>I covered where to ask a few days back and when you do don&#x27;t be surprised if some unrelated comments to your code that could generally improve it. This will especially be true if you have implemented some anti-patterns.<\/p>\n<p>This <a href=\"https:\/\/www.django-antipatterns.com\/\">site<\/a> has a good list of the most common anti-patterns when authoring Django code. Avoiding these anti-patterns will go some way to making your code is easy to read, understand and maintain.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-94","title":"Anti-patterns in Django","summary":"Day 3, here's what not to do","date_modified":"2024-05-20T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#anti-patterns"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-93","content_html":"<p>Yesterday I recommended the initial starting point for learning Django, but once you have completed those, what&#x27;s next?<\/p>\n<p>You have a few options depending on your preference for learning. First up, books. Some members of the community have put out excellent books which go deeper into the details of Django topics you will need to know. Top of the list of these author&#x27;s are <a href=\"https:\/\/learndjango.com\/books\/\">Will Vincent<\/a> and <a href=\"https:\/\/www.mattlayman.com\/understand-django\/\">Matt Layman<\/a><\/p>\n<p>If you don&#x27;t fancy a book, then really it&#x27;s starting to build your own projects. A good starting project is your own personal website, although I suggest this with a couple of caveats, first be prepared to pay for the necessary hosting costs and second do use it as place to learn and write about those learnings not just a line item for the CV.<\/p>\n<p>Finally if writing is not your thing (although I would highly recommend it), build a project that solves a problem or improves an area you actually care about over creating clones of popular website to bulk out a portfolio, a single project created with passion is far more interesting over repeated generic solutions. This style of project will probe you go deeper into Django when you have the need to build a certain feature or fix a bug.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-93","title":"Day 2 of learning Django","summary":"What to do after a tutorial?","date_modified":"2024-05-17T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#first_day","#tutorials","#junior_developer"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-92","content_html":"<p>Disclaimer: This post is one that will likely need to be updated at some point or have revisions when new resources come out!<\/p>\n<p>First and foremost, welcome to Django! This post is intended a sign post to my recommended resources from someone who has been in the community a while.<\/p>\n<p>Your first steps should be a tutorial and the one you choose should depend on how new you are to development in general. The offical tutorial on <a href=\"https:\/\/docs.djangoproject.com\/en\/5.0\/intro\/install\/\">djangoproject.com<\/a> assumes you have a good grasp of Python and general development experience.<\/p>\n<p>If you need a more comphrensive tutorial then I recommend having a look at <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Learn\/Server-side\/Django\">Mozilla&#x27;s tutorial<\/a> which teaches some basics around development as well.<\/p>\n<p>If you get stuck then the best places to get help are the <a href=\"https:\/\/forum.djangoproject.com\/\">Django Forum<\/a> or the <a href=\"https:\/\/discord.gg\/django\">Discord Server<\/a>. Which you choose is down to personal preference of communication styles.<\/p>\n<p>One final point is that, at time of writing, I am not going to recommend any video series since they seem to be often out of date or teach bad practices.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-92","title":"Learning Django from scratch","summary":"Where to start, to get from zero to one.","date_modified":"2024-05-16T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#first_day","#tutorials"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-91","content_html":"<p>The final project layout I will explain for now, is one that is for the simplest of projects. One where building a single Django app is too much boilerplate.\nThis concept is nothing new as <a href=\"https:\/\/www.paulox.net\/2023\/10\/26\/udjango_micro_django\/\">Will Vincent &amp; Paolo Melchiorre<\/a> have shown most recently with \u03bcDjango.<\/p>\n<p>This example does not take it to their extreme, but it&#x27;s usage is more for very simple prototypes or demos.\nTo get started simply add the files typically found in a Django app (eg models, views, migrations etc) directly into your project folder next to your settings file.\nThis directory structure shows what I mean:<\/p>\n<pre class=\"language-diff\"><code class=\"language-diff\">\u251c\u2500\u2500 db.sqlite3\n\u251c\u2500\u2500 manage.py\n\u2514\u2500\u2500 project\n<span class=\"token unchanged\"><span class=\"token prefix unchanged\"> <\/span><span class=\"token line\">\u00a0\u00a0 \u251c\u2500\u2500 __init__.py\n<\/span><span class=\"token prefix unchanged\"> <\/span><span class=\"token line\">\u00a0\u00a0 \u251c\u2500\u2500 asgi.py\n<\/span><span class=\"token prefix unchanged\"> <\/span><span class=\"token line\">\u00a0\u00a0 \u251c\u2500\u2500 models.py\n<\/span><span class=\"token prefix unchanged\"> <\/span><span class=\"token line\">\u00a0\u00a0 \u251c\u2500\u2500 settings.py\n<\/span><span class=\"token prefix unchanged\"> <\/span><span class=\"token line\">\u00a0\u00a0 \u251c\u2500\u2500 urls.py\n<\/span><span class=\"token prefix unchanged\"> <\/span><span class=\"token line\">\u00a0\u00a0 \u251c\u2500\u2500 views.py\n<\/span><span class=\"token prefix unchanged\"> <\/span><span class=\"token line\">\u00a0\u00a0 \u2514\u2500\u2500 wsgi.py\n<\/span><\/span>\n<\/code><\/pre>\n<p>Then simply add the project directory name to <code>INSTALLED_APPS<\/code>:<\/p>\n<pre><code>INSTALLED_APPS = [\n  ...\n  &quot;project&quot;\n]\n<\/code><\/pre>\n<p>and voila you have a working Django project with all the features you need.<\/p>\n<p>Have I missed an obvious project layout? Let me know in the comments on socials or drop me an email!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-91","title":"Common Django Project structures - Part 3","summary":"A very simple setup, useful for demos or very simple prototypes","date_modified":"2024-05-15T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#django_apps"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-90","content_html":"<p>Yesterday we saw the simplest and default pattern for the layout of a Django project. Today we will have a look at the another common layout for larger projects.<\/p>\n<p>This project structure groups all of the apps under a single common directory. This provides a clear boundary of what is an app and what is project based which some might find confusing in the yesterday&#x27;s setup.<\/p>\n<p>Here is an example directory structure:<\/p>\n<pre><code>\u251c\u2500\u2500 db.sqlite3\n\u251c\u2500\u2500 manage.py\n\u251c\u2500\u2500 project\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 __init__.py\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 asgi.py\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 settings.py\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 urls.py\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 wsgi.py\n\u2514\u2500\u2500 apps\n    \u251c\u2500\u2500 app1\n    \u2502   \u251c\u2500\u2500 __init__.py\n    \u2502   \u251c\u2500\u2500 admin.py\n    \u2502   \u251c\u2500\u2500 apps.py\n    \u2502   \u251c\u2500\u2500 migrations\n    \u2502   \u2502\u00a0\u00a0 \u2514\u2500\u2500 __init__.py\n    \u2502   \u251c\u2500\u2500 models.py\n    \u2502   \u251c\u2500\u2500 tests.py\n    \u2502   \u2514\u2500\u2500 views.py\n    \u2514\u2500\u2500 app2\n        \u251c\u2500\u2500 __init__.py\n        \u251c\u2500\u2500 admin.py\n        \u251c\u2500\u2500 apps.py\n        \u251c\u2500\u2500 migrations\n        \u2502\u00a0\u00a0 \u2514\u2500\u2500 __init__.py\n        \u251c\u2500\u2500 models.py\n        \u251c\u2500\u2500 tests.py\n        \u2514\u2500\u2500 views.py\n<\/code><\/pre>\n<p>This structure allows any number of apps to be situated under the <code>apps<\/code> directory without cluttering the project root.<\/p>\n<p>The key thing for this setup is that you need to set the <code>name<\/code> attribute in your <code>AppConfig<\/code> and in <code>INSTALLED_APPS<\/code> to be <code>&quot;apps.app1&quot;<\/code> like so:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token keyword\">class<\/span> <span class=\"token class-name\">MyAppConfig<\/span><span class=\"token punctuation\">(<\/span>AppConfig<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    default_auto_field <span class=\"token operator\">=<\/span> <span class=\"token string\">&quot;django.db.models.BigAutoField&quot;<\/span>\n    name <span class=\"token operator\">=<\/span> <span class=\"token string\">&quot;apps.app1&quot;<\/span>\n<\/code><\/pre>\n<pre class=\"language-py\"><code class=\"language-py\">INSTALED_APPS <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">[<\/span>\n  <span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span>\n  <span class=\"token string\">&quot;apps.app1&quot;<\/span><span class=\"token punctuation\">,<\/span>\n<span class=\"token punctuation\">]<\/span>\n<\/code><\/pre>\n<p>Where <code>app1<\/code> is the current app name so the second app would have the value <code>&quot;apps.app2&quot;<\/code>.<\/p>\n<p>Tomorrow I will show what structure you can do for the simplest of projects.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-90","title":"Common Django Project structures - Part 2","summary":"Nested structures for more complex projects","date_modified":"2024-05-14T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#django_apps"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-89","content_html":"<p>There are an infinite number of ways you could structure a Django project, from the simple to the sublimely complex, and each would mean configuring app names or configuration slightly differently.<\/p>\n<p>The first and most typical version is the flat directory structure. Here the Django project folder is a sibling to each different app you might have:<\/p>\n<pre><code>\u251c\u2500\u2500 db.sqlite3\n\u251c\u2500\u2500 manage.py\n\u251c\u2500\u2500 project\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 __init__.py\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 asgi.py\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 settings.py\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 urls.py\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 wsgi.py\n\u2514\u2500\u2500 app\n    \u251c\u2500\u2500 __init__.py\n    \u251c\u2500\u2500 admin.py\n    \u251c\u2500\u2500 apps.py\n    \u251c\u2500\u2500 migrations\n    \u2502\u00a0\u00a0 \u2514\u2500\u2500 __init__.py\n    \u251c\u2500\u2500 models.py\n    \u251c\u2500\u2500 tests.py\n    \u2514\u2500\u2500 views.py\n<\/code><\/pre>\n<p>Then the name in your <code>AppConfig<\/code> and in <code>INSTALLED_APPS<\/code> is simply just <code>app<\/code> like so:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token keyword\">class<\/span> <span class=\"token class-name\">MyAppConfig<\/span><span class=\"token punctuation\">(<\/span>AppConfig<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    default_auto_field <span class=\"token operator\">=<\/span> <span class=\"token string\">&quot;django.db.models.BigAutoField&quot;<\/span>\n    name <span class=\"token operator\">=<\/span> <span class=\"token string\">&quot;app&quot;<\/span>\n<\/code><\/pre>\n<pre class=\"language-py\"><code class=\"language-py\">INSTALED_APPS <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">[<\/span>\n  <span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span>\n  <span class=\"token string\">&#x27;app&#x27;<\/span><span class=\"token punctuation\">,<\/span>\n<span class=\"token punctuation\">]<\/span>\n<\/code><\/pre>\n<p>Personally I find this project fine for when you have one or two apps, but for a more complex I prefer to have some common root directories which I will share tomorrow!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-89","title":"Common Django Project structures","summary":"and how to name your apps","date_modified":"2024-05-13T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#django_apps"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-88","content_html":"<p>Today I hosted the latest Cambridge Django Social which was Brunch co-working at Stir, a local bakery\/caf\u00e9.\nFood and drink was enjoyed by all especially the two french members who were confused about what brunch for the English looks like!<\/p>\n<p>Josh from Foxley graced us with his presence, excellent conversation (mostly around recruitment of Django engineers!) and stickers for me to give out at future events.\nHe also asked for updates to the django.social website which I have been working on in my limited spare time. They want it live for DjangoCon EU in a few weeks time,\nso nothing like a bit of public accountability to get me moving on it! I have a deadline of the beginning of June!<\/p>\n<p>Next month (June 21st) we&#x27;re going for a picnic with games in one of Cambridge&#x27;s lovely green spaces. See you then if your local or passing through!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-88","title":"Django Social Cambridge","summary":"Another month, another event!","date_modified":"2024-05-10T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#django.social"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-87","content_html":"<p>Recently I bought myself a new laptop, which should mean a productivity boost to me so fan&#x27;s don&#x27;t run so often or generrally overheats.\nI am also taking the time to do some spring cleaning and get more structured in how I organise my laptop environment as recommended on Mastodon.<\/p>\n<p>To begin with I have cleared out any unused configuration files and applications that no longer work or I have never used.\nSecondly I have started my own dotfiles repo (which I will share in due course), which honestly feels overwhelming and a rabbit hole when browsing other peoples dotfiles repos.\nThere are new tools to explore and evaluate and new options in existing tools or replacement tooling. All in all results in decision fatigue.<\/p>\n<p>There is an opportunity here though, a tool that would guide a user through creating a dotfiles repo or even run some of the command automatically to generate the required files (eg <code>brew bundle<\/code> for mac)\nThis would minimise the choice and help someone actually get started. For me I took a break and knew I had to be more explicit in what I was trying to get done, this almost always unblocks me.\nI didn&#x27;t have a plan, now I do. Fingers crossed I can now migrate quicker!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-87","title":"Migrating to my new laptop","summary":"Time to get my digital house in order","date_modified":"2024-05-09T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#dotfiles","#migration","#laptops"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-86","content_html":"<p>For better or worse, Artificial Intelligence is currently experiencing a peak of the hype cycle of technology, more specificially LLMs or Generative AI. (Yay for buzzwords &amp; TLAs!)\nIt&#x27;s being misused, over sold that these AI can solve all our problems (spoiler: they won&#x27;t) and replace most of the jobs available. The truth is of course more nuanced.<\/p>\n<p>A better framing for the AI of today would be <strong>Augmented<\/strong> Intelligence. The terminology is there with quite a few of the &#x27;AI&#x27; products out there, Github&#x27;s Copilot being the most prominent.\nThey will make those already productive and with understanding faster to produce the necessary output, they give an excellent starting point, they are a partner in the creative work not a replacement for the person.<\/p>\n<p>Remember that you, the human, need to understand and iterate on the input &amp; output of an AI. It&#x27;s work alone will not be complete by itself.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-86","title":"We are in the age of AI not AI","summary":"Confused by that title? Then let me explain.","date_modified":"2024-05-08T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#llm","#ai"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-85","content_html":"<p>Dear Junior Developer,<\/p>\n<p>You are learning to code in a time when LLM&#x27;s are booming and providing easy high contextual answers to the queries you have.\nThey have what appears to be an unlimited amount of knowledge and can produce code for almost anything you ask.\nBut do they understand what they are producing, the wider context of your problem? Is the code even valid for today&#x27;s task?<\/p>\n<p>The responsibilty of understanding code still falls upon your shoulders, the theory behind the code, can you make sense of it?\nWhen (not if) a bug happens could you find the error, can you understand what the error message is telling you?\nOr would you just copy and paste some more code from ChatGPT or continue to press tab from co-pilot?<\/p>\n<p>Knowledge is very different from Understanding, computers or AI in whatever form will likely always have more knowledge.\nBut they will always lack the understanding required to meet the exact requirements at hand, that is where we humans, we developers fill the gap.<\/p>\n<p>You need to fill the gap and <em>understand<\/em> what you are building.<\/p>\n<p>A Senior Developer<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-85","title":"Dear Junior Developer...","summary":"Understand the difference between Knowledge and understanding","date_modified":"2024-05-07T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#mentoring","#dear_junior","#llm"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-84","content_html":"<p>I once had the chance to start a new project and codebase in one of my jobs. A fresh Django codebase from scratch that was going to be an API for a mobile app.\nAs a result myself &amp; the other developer decided to implement an abstract BaseModel with a few handy fields and some common functionality.\nThis functionality included the soft-delete mixins and managers from django-model-utils.<\/p>\n<p>Years later when this codebase had been in production for a while, a new colleague was debugging some issue and found that the soft-delete was causing a host of issues which prompted him to submit <a href=\"https:\/\/github.com\/jazzband\/django-model-utils\/issues\/364\">this issue<\/a>\nDo have a read because it&#x27;s a good warning when interacting with Django&#x27;s default manager.<\/p>\n<p>But long story short, if you do need some sort of soft-delete functionality in your codebase, then consider the inverse like a field called <code>is_available<\/code> over <code>is_deleted<\/code> and add extra managers (eg <code>available_objects<\/code>) instead of overriding the default<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-84","title":"A cautionary tail of adding soft delete to Models","summary":"Most of the time you aren't going to need it, but be careful if you do.","date_modified":"2024-05-06T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#models","#model-utils","#soft_delete"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-83","content_html":"<p>The next component of Django to cover is another key component of building a website, URLs or more precisely URL patterns. There are two types of URLs in Django, static and dynamic.<\/p>\n<p>Static urls are fixed (eg <code>\/about<\/code> or <code>\/team<\/code>). these are the simplest urls to implement as follows:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\">  path<span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;about&#x27;<\/span><span class=\"token punctuation\">,<\/span> views<span class=\"token punctuation\">.<\/span>about<span class=\"token punctuation\">,<\/span> name<span class=\"token operator\">=<\/span><span class=\"token string\">&#x27;about&#x27;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n  path<span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;team&#x27;<\/span><span class=\"token punctuation\">,<\/span> views<span class=\"token punctuation\">.<\/span>team<span class=\"token punctuation\">,<\/span> name<span class=\"token operator\">=<\/span><span class=\"token string\">&#x27;team&#x27;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n<\/code><\/pre>\n<p>Dynamic urls can capture certain aspects of a URL as a variable to pass to the view. For example looking at a profile page or an item in an online store. Variable capture includes strings, integers, slugs, uuid, paths or a custom regex.\nThis is used as follows:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\">    path<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;articles\/&lt;int:year&gt;\/&quot;<\/span><span class=\"token punctuation\">,<\/span> views<span class=\"token punctuation\">.<\/span>year_archive<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n    path<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;articles\/&lt;int:year&gt;\/&lt;int:month&gt;\/&quot;<\/span><span class=\"token punctuation\">,<\/span> views<span class=\"token punctuation\">.<\/span>month_archive<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n<\/code><\/pre>\n<p>Here we have 2 urls, the first has a single argument named <code>year<\/code> that is passed as an integer to the view and the second view passed <code>year<\/code> and <code>month<\/code> to a view as integers.<\/p>\n<p>Finally Django URLs have the <code>include<\/code> function, this allows the nesting of urlpatterns under a common prefix. This is useful for simple organisation or the reuse of a set of URLs in multiple contexts (eg reusable apps), for example:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token comment\"># in articles\/urls.py<\/span>\nurl_patterns <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">[<\/span>\n    path<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;&lt;int:year&gt;\/&quot;<\/span><span class=\"token punctuation\">,<\/span> views<span class=\"token punctuation\">.<\/span>year_archive<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n    path<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;&lt;int:year&gt;\/&lt;int:month&gt;\/&quot;<\/span><span class=\"token punctuation\">,<\/span> views<span class=\"token punctuation\">.<\/span>month_archive<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n<span class=\"token punctuation\">]<\/span>\n\n<span class=\"token comment\"># in project\/urls.py<\/span>\n  path<span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;articles\/&#x27;<\/span><span class=\"token punctuation\">,<\/span> include<span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;articles.urls&#x27;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-83","title":"Django URLs in 100 words","summary":"Getting from the browser to the view code","date_modified":"2024-05-03T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#urls"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-82","content_html":"<p>Today we return to the ongoing series of explaining each aspect of Django in approximately 100 words. Today we cover views.<\/p>\n<p>Views are the core of Django handling web requests. They are defined as a callable which takes a request object and optional arguments and returns a response object.\nDjango provides some helpers to easily render HTML via templates as a response. Views are responsible for doing logic, they should not be used for presentation.\nSome of the things you will can do in a view include database queries, form validation, API calls or anything that Python can do.<\/p>\n<p>Views are just plain python with very little extra. Recently async views were added to Django expanding the responsiveness to browser.\nFinally over time you will find that views start larger and become smaller as other Django constructs are used to handle the logic and views are simply wiring up URLs to that logic.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-82","title":"Django Views in 100 words","summary":"A core component in Django's MVT framework","date_modified":"2024-05-02T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#views"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-81","content_html":"<p>A quick meta post today to celebrate the new month! Recently I have added two new features surrounding theses posts, tags and series.<\/p>\n<p>I have tagged each post with a series of tags which provide a set of categories that is relates to.\nThese are now displayed on each post as a link which will take a user to a page with all the other posts tagged with that particular tag.\nThis works basically as one would expect. In future I may also add a search feature to the posts. These tags also get added to the RSS feed as categories which I use as hashtags when posting to social media.<\/p>\n<p>The next feature is series. Since some topics cover multiple days\/posts I wanted a easy way for a user to browse to the other posts in the series.\nThis involves adding the title of the series to each post which acts as a key to find the other posts in the series, we then render the series title and other titles as a table of contents on page.\nThere is more I could likely do here, but for now it works sufficiently well.<\/p>\n<p>If your interested in a more technical deep dive of this then let me know, for context the site is a NextJS site, not Django!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-81","title":"Recent improvements to the website","summary":"Tags and Series of posts","date_modified":"2024-05-01T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#meta","#website_improvements","#nextjs"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-80","content_html":"<p>So after posting my idea from <a href=\"\/100-words\/day-79\">yesterday<\/a>, I had a few responses on Mastodon which I thought would be worth sharing today.<\/p>\n<p>First up is Tim sharing <a href=\"https:\/\/deadmanssnitch.com\/\">Dead Man&#x27;s Snitch<\/a> which solves the alerting part of the of the idea, but isn&#x27;t Django native.\nNext is <a href=\"https:\/\/indiehackers.social\/@danielquinn@mastodon.social\/112356817075104319\">Dan<\/a> suggesting a possible API of how it could work.\nBruno suggests a similar setup of a Model to store the query, then a management command combined with either celery or cron to run the queries. He also highlighted the possible security risks with running arbitary SQL.<\/p>\n<p>Bruno also suggested the excellent <a href=\"https:\/\/www.metabase.com\/\">Metabase<\/a> which works as a separate hosted or self-hosted solution but is completely independent of Django and it&#x27;s ORM and any code you might have added on top.\nFinally, this post inspired <a href=\"https:\/\/indiehackers.social\/@boxed@mastodon.social\/112358815363702568\">Anders<\/a> to write up his system for dealing with this. It tickes all the boxes except storing the query in a model, opting for having it stored in code,\nwhich in all honesty is safer for multiple obvious reasons.<\/p>\n<p>There is the makings of a package here. Probably starting with Ander&#x27;s post and stripping out the specific packages used to be plain Django.<\/p>\n<p>Thanks to Tim, Dan, Bruno &amp; Anders for the responses!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-80","title":"Native Django alerting package - Responses","summary":"Responses to yesterday's post","date_modified":"2024-04-30T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#package","#idea-response"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-79","content_html":"<p>Here&#x27;s another idea for a django package I would like to see (which is code for I may build this one day...) or someone will tell me it already exists.<\/p>\n<p>The other week I was debugging a issue with a client and had nailed the ad-hoc query to pull out some rows that needed updating.\nI then proceeded to fix them and all was well with the world. But then a thought occurred to me that I would like to get notified if this query was ever non-zero again.\nIn fact I check the query again today and it was non-zero, so I still have a bug to track down.<\/p>\n<p>On to the package idea, I would like to easily store a query (ideally as a Django ORM query) and a threshold and\/or comparison.\nThen these queries would get checked regularly and would notify something when the threshold was reached or exceeded.<\/p>\n<p>I do understand that the description is fairly vague and it might not actually be a package and probably more a service, especially given some of the likely infrastructure requirements\nBut it would have a nice simplicity as a package that could be integrated into an existing project.<\/p>\n<p>What do you think? Does a package exist like this? or am I just looking for a monitoring service?<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-79","title":"Native Django alerting package","summary":"Get notified for changes to arbitary queries","date_modified":"2024-04-29T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#package","#idea"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-78","content_html":"<p>The hidden power of Django is the number of third-party packages available for free and many of them are must have&#x27;s to any project to fill in functionality not in core or to extra functionality that would not be appropriate to be in core.\nAdditionally when it comes to feature requests for core, the response is typically one of getting consenus on the forum and producing a third-party package to gauge interest in the feature.<\/p>\n<p>Today, I have some (hopefully) constructive thoughts &amp; questions around the second half of that response. The main question goes something like this: How do we design a package that merging it would be (relatively) easy?<\/p>\n<p>Two very popular packages come to my mind in regard to this question, Django Rest Framework and Django allauth.\nBoth provide excellent code and functionality that add to or extend Django&#x27;s core offering and both have functionality that many have likely suggested to exist in core (DRF definitely, allauth possibly) especially to consider the batteries-included approach of Django.<\/p>\n<p>However neither would ever get merged in core as is. There is too much for a single ticket\/PR to even contemplate, so obviously if we wanted to merge in one of these packages they would need to be broken down into phases.\nFor example DRF could go broadly something like: Content Negotiation, Serializers, Views(sets) &amp; routers (thanks Carlton!).<\/p>\n<p>So what is DRF had been designed &amp; built as a series of smaller packages that incrementally got merged in over time? Then a virtual package which brought them all together?\nI realised this is easy to say in hindsight although allauth has recently split it&#x27;s social stuff into a separate virtual package.<\/p>\n<p>I don&#x27;t have any answers, just questions! How could we setup packages so they could easily be merged? Is it the size and scope of a package? Is is the quality of code or the python API design?<\/p>\n<p>One final note on this. I am not remotely suggesting that this be applicable to all packages, but I would love to see a set of guidelines that help those in the position of a feature request.<\/p>\n<p>What do you think?<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-78","title":"Merging a third party package into django core","summary":"Question: What would a package look like if it's goal was to be merged into Django core?","date_modified":"2024-04-26T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#packages","#merging"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-77","content_html":"<p>I am currently doing some work for a client to integrate Xero (accounting platform) into my client&#x27;s Django app.\nXero is not listed as a supported provider listed in allauth&#x27;s documentation so I rolled up my sleeves and prepared a working custom provider, then started a draft PR upstream last week.<\/p>\n<p>Then Raymond yesterday asked if this could be achieved with allauth&#x27;s openid_connect (OIDC) provider, which upon closer inspection is a more generic provider that can deal with websites that support it.\nAfter a day changing the config, fixing a few bugs, it turns out that this worked and my custom provider will not be required.<\/p>\n<p>I have suggested some documentation updates which I will happily do the work for if Raymond likes the idea which is to essentially highlight the openid_connect provider more prominently.\nThe lesson here is to check the generic providers within allauth before charging ahead with a custom provider.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-77","title":"Unsupported provider in allauth? Try openid_connect","summary":"Try this before starting a fork and submitting a PR","date_modified":"2024-04-25T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#allauth","#openid_connect"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-76","content_html":"<p>Another day, another beginner wanting to use GMail for free email sending, while this is entirely valid for a small project, please don&#x27;t do this for your next startup. These days use a cloud service like AWS SES, Sendgrid or Mailgun.<\/p>\n<p>With the above disclaimer out the way, this is how you send email using Gmail. First your email settings in settings.py needs to look as follows:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\">EMAIL_BACKEND <span class=\"token operator\">=<\/span> <span class=\"token string\">&quot;django.core.mail.backends.smtp.EmailBackend&quot;<\/span>\nEMAIL_HOST <span class=\"token operator\">=<\/span> <span class=\"token string\">&#x27;smtp.gmail.com&#x27;<\/span>\nEMAIL_PORT <span class=\"token operator\">=<\/span> <span class=\"token number\">587<\/span>\nEMAIL_USE_TLS <span class=\"token operator\">=<\/span> <span class=\"token boolean\">True<\/span>\nEMAIL_HOST_USER <span class=\"token operator\">=<\/span> <span class=\"token string\">&quot;your.app.email.replace.me@gmail.com&quot;<\/span>\nEMAIL_HOST_PASSWORD <span class=\"token operator\">=<\/span> <span class=\"token string\">&#x27;&lt;see next paragraph for this&gt;&#x27;<\/span>\n<\/code><\/pre>\n<p>If you use your default password that you sign in with you will get an error like this:<\/p>\n<pre><code>SMTPAuthenticationError at \/\n(535, b&#x27;5.7.8 Username and Password not accepted. For more information, go to\\n5.7.8  https:\/\/support.google.com\/mail\/?p=BadCredentials l9-20020a17090615c900b00a555be38aaasm7938755ejd.164 - gsmtp&#x27;)\n<\/code><\/pre>\n<p>To solve this you first need to ensure that the Google account has multi-factor authentication setup correctly, either with a device and\/or authenticator app codes.\nThen you need and &quot;App Password&quot;, to create one visit <a href=\"https:\/\/myaccount.google.com\/apppasswords\">this link<\/a>, add a name for your password, click &quot;Create&quot;, then copy the 16 characters that appear on the screen in to the password setting with no spaces.<\/p>\n<pre class=\"language-py\"><code class=\"language-py\">EMAIL_HOST_PASSWORD <span class=\"token operator\">=<\/span> <span class=\"token string\">&#x27;abcdefghijklmnop&#x27;<\/span>\n<\/code><\/pre>\n<p>You should then be able to send email via GMail. Finally revisiting my first sentence, below are a list of email providers that I would recommend instead, expect to pay some money at some point as with most things when it comes to hosting an application:<\/p>\n<ul>\n<li><a href=\"https:\/\/www.mailgun.com\/\">Mailgun<\/a><\/li>\n<li><a href=\"https:\/\/sendgrid.com\/en-us\">Sendgrid<\/a><\/li>\n<li><a href=\"https:\/\/aws.amazon.com\/ses\/\">AWS SES<\/a><\/li>\n<li><a href=\"https:\/\/mailchimp.com\/features\/transactional-email\/\">Mailchimp<\/a><\/li>\n<li><a href=\"https:\/\/postmarkapp.com\/\">Postmark<\/a><\/li>\n<li><a href=\"https:\/\/userlist.com\/\">Userlist<\/a><\/li>\n<li><a href=\"https:\/\/resend.com\/\">Resend<\/a><\/li>\n<li><a href=\"https:\/\/forwardemail.net\/en\">Forward Email<\/a><\/li>\n<li>and many more through customer automation platforms like Intercom or the larger CRM platforms like Hubspot<\/li>\n<\/ul>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-76","title":"Sending email in Django using GMail","summary":"While I don't advise it, here's how.","date_modified":"2024-04-24T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#gmail","#email"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-75","content_html":"<p>A question that often gets asked by newbies and perhaps those under pressure to deliver a finished product is &quot;How can I get the admin to do X?&quot; or &quot;How do I customise the admin?&quot;<\/p>\n<p>The Django Admin is an amazing tool that we get for free and while a bit dated in it&#x27;s look and feel, it is definitely something you would have to pay for in other circumstances.\nThat said it is not your frontend, it should not be exposed to normal users and ideally staff ought to have limited access. Ideally it ought to be just for the development team to make easy data fixes.<\/p>\n<p>The reality in most codebases is that it&#x27;s a great start for internal staff users to get stuff done with the database and fix issues with customer data or add in content to the site.<\/p>\n<p>The time to move away from the admin is when the following starts to happen:<\/p>\n<ul>\n<li>You start to heavily customise templates to meet the needs of non technical users<\/li>\n<li>You need to implement a custom workflow that covers multiples consecutive steps or models.<\/li>\n<li>You have strict compliance needs that need to be addressed.<\/li>\n<\/ul>\n<p>At this point make sure to give yourself enough time and capacity to recreate the pages your internal users need and previously had in the Django admin.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-75","title":"When should the default admin stop being the default","summary":"It does a lot out of the box, but it's not a solution for every need.","date_modified":"2024-04-23T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#admin"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-74","content_html":"<p>So the title is a bit click baity! Anyways I was reminded that this is possible when updating some git config last week.\nThe reason you might want this is not for an actual message but to customise the comment that appears when you type <code>git commit<\/code> which would then bring up your editor.\nMy default message has a reminder of what makes a good commit message, which I rarely read... Perhaps a better options is some examples which I could comment out.<\/p>\n<p>Achieving this is very simple, drop a file called <code>.gitmessage<\/code> in your home directory and edit away! Below is a quick script snippet to get you started.<\/p>\n<pre class=\"language-shell\"><code class=\"language-shell\"><span class=\"token builtin class-name\">echo<\/span> <span class=\"token string\">&quot;# Title: Summary, imperative, start upper case, don&#x27;t end with a period\n# No more than 50 chars. #### 50 chars is here:  #\n\n# Remember blank line between title and body.\n\n# Body: Explain *what* and *why* (not *how*). Include task ID (Jira issue).\n# Wrap at 72 chars. ################################## which is here:  #\n\n\n# At the end: Include Co-authored-by for all contributors.\n# Include at least one empty line before it. Format:\n# Co-authored-by: name &lt;user@users.noreply.github.com&gt;\n#\n# How to Write a Git Commit Message:\n# https:\/\/chris.beams.io\/posts\/git-commit\/\n#\n# 1. Separate subject from body with a blank line\n# 2. Limit the subject line to 50 characters\n# 3. Capitalize the subject line\n# 4. Do not end the subject line with a period\n# 5. Use the imperative mood in the subject line\n# 6. Wrap the body at 72 characters\n# 7. Use the body to explain what and why vs. how&quot;<\/span> <span class=\"token operator\">&gt;<\/span> ~\/.gitmessage\n<\/code><\/pre>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-74","title":"Creating a default git commit message","summary":"Why in the world would you want to have a default message?","date_modified":"2024-04-22T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#git","#defaults","#commits"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-73","content_html":"<p>I asked this <a href=\"https:\/\/indiehackers.social\/@nanorepublica\/110628334785163922\">question<\/a> shortly after DjangoConEU 2023 and got some interesting responses from the community.\nBroadly it would seem the places to start when approaching an existing codebase for the first time is the settings file, then URLS and dependencies. After this it&#x27;s views, tests and models.<\/p>\n<p>From a tools perspective django-debug-toolbar, kolo.app and IDE tooling are used to quickly understand what is going. That said I do think there is some gaps in the tooling that could be filled. I mentioned one the other <a href=\"\/100-words\/day-71\">day<\/a>.\nAnother idea along a similar line would be getting attempting to get a complete understanding of the CSS coverage (assuming you are not using something like TailwindCSS) for a project.\nI think this should be possible given we could render every template across a project, then analysing which CSS statements are being used or not (media queries would be tricky though!).<\/p>\n<p>I also wonder if there are some sensible hueristics that could help, for example ensuring the length of a file doesn&#x27;t exceed a certain amount of lines? Which I am sure existing linting tools cover.<\/p>\n<p>What other ideas would you like to see help you out? Or are there any other tools that I have missed?<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-73","title":"How would you audit a Django codebase?","summary":"or better yet, what's available to get familiar with a codebase.","date_modified":"2024-04-19T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#audit"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-72","content_html":"<p>I recently posted a question\/poll on <a href=\"https:\/\/indiehackers.social\/@nanorepublica\/112226584130287024\">Mastodon<\/a> asking about the usage of <code>startproject<\/code> and <code>startapp<\/code>.\nMy hypothesis is that these commands are more often used by those with less experience and those with more experience will have there own methods of achieving the same thing (eg cookiecutter or similar).<\/p>\n<p>Currently I don&#x27;t yet have enough data to draw any specific conclusions, but I will layout why I think it is important to validate the above statement, specifically in regard to the <code>startproject<\/code> and <code>startapp<\/code>.\nI have seen fair bit of back and forth about changes to the default templates on the forum and there have been tickets in the past to the same effect.\nMy current belief is that these commands with the default template are trying to serve everyone and therefore are not serving any one group effectively and that they ought to, by default, be targeting beginners to Django.\nTherefore this means adding settings and folders in an opiniated manner so that common tasks &quot;just work&quot;.\nIn particular static settings, media &amp; templates come to mind, but really, in my opinion, once we have a defined audience progress will be quicker.<\/p>\n<p>Who do you think <code>startproject<\/code> and <code>startapp<\/code> are meant for? Is it you or someone else?<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-72","title":"Who is the primary user for startproject & startapp?","summary":"The classic question: What are we optimising for?","date_modified":"2024-04-18T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#startproject","#startapp","#management_commands"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-71","content_html":"<p>This is an idea that has been floating around my head for a long time in various forms.\nCurrently it has been crystalised by me working on a codebase that has a lot of cruft such that URL patterns, views and templates exist, but are not actually currently used.<\/p>\n<p>At a high-level the tool would use the following process:<\/p>\n<ol>\n<li>Analyse all templates, looking for plain internal <code>href<\/code> attributes and the <code>url<\/code> tag and then report these.<\/li>\n<li>Then it would collate the urls and match them to the currently defined urlpatterns<\/li>\n<li>Which in turn is linked to views<\/li>\n<li>Which will mention template names again<\/li>\n<\/ol>\n<p>From the following process the tool could then annotate files or produce a report that would list:<\/p>\n<ol>\n<li>Which views are referenced by a URL (and how many times)<\/li>\n<li>Which URLs are referenced in a template<\/li>\n<li>Which templates are referenced by views.<\/li>\n<\/ol>\n<p>This is just the start of course, there are going to be templates from <code>include<\/code> and <code>extend<\/code> which would could be reported on during the template process or URLs referenced by <code>reverse<\/code> or <code>redirect<\/code> in other parts of the codebase.<\/p>\n<p>I think this could be invaluable of getting a birds-eye view of a Django codebase, what do you think?<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-71","title":"Analysis for unused code in a Django codebase","summary":"An idea I wish existed and I hope to build one day","date_modified":"2024-04-17T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#idea","#package","#static_analysis"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-70","content_html":"<p>When working on a project, branching for features is very common. However features often require changes to the database (new fields, updated fields or new models),\nthis is where state changes gets introduced and it becomes tricky to switch or merge branches. Below is my goto advice for dealing with this situation.<\/p>\n<p>First ensure that the feature branch is up to date before merging. This can be enforced in Github and Gitlab. Then it&#x27;s a set of steps to follow before doing your final merge of a branch.<\/p>\n<ol>\n<li>Pull in the latest changes from the main branch, if your migrations are the latest then you are good to merge.<\/li>\n<li>However if there are conflicting migrations, then rollback your migrations and delete or move your migrations files. The move should only be in the case if data migrations are present.<\/li>\n<li>Run <code>migrate<\/code> on the existing migrations.<\/li>\n<li>Then run <code>makemigrations<\/code> to generate new migrations for your changes or copy your existing migrations back into the project and rename appropriately and update the migration dependencies<\/li>\n<li>Run <code>migrate<\/code> again to check everything still works as before and you&#x27;re done.<\/li>\n<\/ol>\n<p>The above should result in a linear migration history and at this point I will give a shout out to Adam Johnson&#x27;s package <a href=\"https:\/\/github.com\/adamchainz\/django-linear-migrations\">django-linear-migrations<\/a> which by the looks of it\nprovides some excellent tooling to enforce a linear migration history.<\/p>\n<p>Other strategies that will help is to leverage multiple apps as a team is then less likely that two people will be working on the same app at the same time which would mean Django&#x27;s migration dependencies kick in to help keep the history in order<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-70","title":"Dealing with migrations on a team","summary":"Code branches are easy, but what about state branches?","date_modified":"2024-04-16T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#migrations"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-69","content_html":"<p>This is an idea that been rattling around my head for a few weeks. As a moderator on the Django Discord Server, we have some informal processes about nominating new helpers and moderators.\nBut it strikes me that I have no idea if any such process exists for the forum as they currently operate very independently from each other.\nThis in itself is not inheritantly wrong or bad, but I do believe there is likely some room for improvement as well as setting up some boundaries about what belongs in these community areas and what requires it&#x27;s own space.<\/p>\n<p>Generally the idea is a working group could work to set out these boundaries, some processes within the platforms (permission elevation, succession of roles), moderation policies &amp; budgets available to keep the current platforms running.\nThere could also be scope for improving these platform with time and\/or budget.<\/p>\n<p>I guess one way of thinking about the concept for this group is that it&#x27;s an inward facing version of the Social Media working group, ensuring that the community and the tools we use are fit for purpose and serve the community as a whole.<\/p>\n<p>What do you think? Let me know on Mastodon.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-69","title":"Working Group idea: Community","summary":"Some thoughts on a potential new working group","date_modified":"2024-04-15T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#working_groups","#community"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-68","content_html":"<p>I recently suggested an idea on the Django Discord server, that is would be nice to get alerts when djangoproject.com is having issues.\nTobias on the Ops team provided some pointers in regard to what they already have setup and so here is the prototyping I have done to date, which will hopefully get implemented soon (I am doing some final private testing today).<\/p>\n<p>The first job is to get a Grafana Cloud account (or use an existing one) and then setup some &quot;Synthentic Checks&quot;. Given you can only set one Alert per check, multiple might be needed.\nBy default Grafana has 3 alerts (Low, Medium, High) so three checks would seem sensible.<\/p>\n<img alt=\"Configuring Alerts in Synthetic Checks\" loading=\"lazy\" width=\"1338\" height=\"482\" decoding=\"async\" data-nimg=\"1\" style=\"color:transparent\" srcSet=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Fchecks-alerts.78abd1ab.png&amp;w=1920&amp;q=75 1x, \/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Fchecks-alerts.78abd1ab.png&amp;w=3840&amp;q=75 2x\" src=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Fchecks-alerts.78abd1ab.png&amp;w=3840&amp;q=75\"\/>\n<p>Next is to go into &quot;Alerts &amp; IRM&quot; and setup Discord as a Contact Point. Although be sure to set the alertmanager at your cluster over the default Grafana manager (this <a href=\"https:\/\/community.grafana.com\/t\/synthetic-checks-do-not-trigger-oncall-alerts\/113015\">forum post<\/a> helped).\nThis involves getting a webhook from Discord related to the channel you would like to notify to.<\/p>\n<img alt=\"Specifying the Grafana Alert Manager\" loading=\"lazy\" width=\"846\" height=\"252\" decoding=\"async\" data-nimg=\"1\" style=\"color:transparent\" srcSet=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Falert-manager.520f218b.png&amp;w=1080&amp;q=75 1x, \/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Falert-manager.520f218b.png&amp;w=1920&amp;q=75 2x\" src=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Falert-manager.520f218b.png&amp;w=1920&amp;q=75\"\/>\n<p>Third is to add a notifications template as the default notification is very verbose. I followed <a href=\"https:\/\/faun.pub\/overview-of-grafana-alerting-and-message-templating-for-slack-6bb740ec44af\">this article<\/a> based on Slack as a starting point<\/p>\n<img alt=\"Configuring Contact Point with additional options\" loading=\"lazy\" width=\"2508\" height=\"1396\" decoding=\"async\" data-nimg=\"1\" style=\"color:transparent\" srcSet=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Fcontact-point.f37e914e.png&amp;w=3840&amp;q=75 1x\" src=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Fcontact-point.f37e914e.png&amp;w=3840&amp;q=75\"\/>\n<p>Finally is configuring the &quot;Notifications Policies&quot; to use Discord as the contact point. You may have existing policies in place so take care to ensure that existing policies don&#x27;t get altered.<\/p>\n<img alt=\"Configuring Notifications Policy\" loading=\"lazy\" width=\"1552\" height=\"734\" decoding=\"async\" data-nimg=\"1\" style=\"color:transparent\" srcSet=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Fnotifications-policy.739ef23e.png&amp;w=1920&amp;q=75 1x, \/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Fnotifications-policy.739ef23e.png&amp;w=3840&amp;q=75 2x\" src=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Fnotifications-policy.739ef23e.png&amp;w=3840&amp;q=75\"\/>\n<p>I hope to get this setup soon, but I personally don&#x27;t have all the access to the necessary accounts (namely the Django grafana account). But hopefully by the end of next week we might have alerting setup in Discord!\nNext is to have a grafana public status page for Django.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-68","title":"Setting up Discord notifications in Grafana","summary":"Prototyping better visibility for djangoproject.com","date_modified":"2024-04-12T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#devops","#django","#monitoring","#alerting"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-67","content_html":"<p>I was catching up on some long overdue reading and <a href=\"https:\/\/matklad.github.io\/2021\/02\/06\/ARCHITECTURE.md.html\">this article<\/a> from a Django newsletter caught my eye.\nThis reminded of a recent idea for a similar file to live in a codebase.<\/p>\n<p>This file is called PATTERNS.md. The purpose of this file is to describe the patterns and conventions that exist in a project that may tie to the architecture, but are too fine grained to include in a high-level document.\nExamples would include database field name conventions and relevant code to support them, method names on classes, where business logic should live or class mixins that are available.<\/p>\n<p>The event that birthed this idea was me inheriting a codebase and creating a new model with a field named <code>is_active<\/code>, this is reasonable, but I later realised that previous developers had named fields <code>is_available<\/code> and had put in extra work with a BaseManager and Queryset for the functionality\nEssentially we want to surface patterns that exist across the codebase to ensure the codebase remains consistent across time and developers.<\/p>\n<p>While I write this I reminded of another a <a href=\"https:\/\/lukeplant.me.uk\/blog\/posts\/enforcing-conventions-in-django-projects-with-introspection\/\">recent article<\/a> from Luke Plant where he addresses the same concerns in regard to DateTime Fields\nusing the checks framework in Django. This file could be seen as an language agnostic version of that or a first step towards using a similar method that he outlines.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-67","title":"Patterns.md","summary":"A file to keep useful notes about a project","date_modified":"2024-04-11T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#documentation","#design_patterns","#naming"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-66","content_html":"<p>When it comes to errors and exceptions, it is easy to panic and feel like the error message is providing no helpful information at all.\nWhen this happens I find it useful to slow down and read the message very slowly. Error messages are unlikely to contain extra words, they are direct and to the point.\nEach word is significant and has a purpose in being present.<\/p>\n<p>Let&#x27;s take an example message and break it down to understand what the message is communicating.<\/p>\n<pre><code>django.urls.exceptions.NoReverseMatch: Reverse for &#x27;home&#x27; not found. &#x27;home&#x27; is not a valid view function or pattern name.\n\nPart 1: django.urls.exceptions.NoReverseMatch\nPart 2: Reverse for &#x27;home&#x27; not found.\nPart 3: &#x27;home&#x27; is not a valid view function or pattern name.\n<\/code><\/pre>\n<p>The first part of the message is the type of excption being raised. This provides a clue as to what is wrong, here NoReverseMatch shows it&#x27;s something to do with reversing and urls.<\/p>\n<p>The next sentence clearly tells what is wrong, Django hasn&#x27;t found a url named &#x27;home&#x27;. Finally this last sentence further explains the problem or might provide a potential solution.\nIn the above example, Django is telling us that it is specifically looking for a view function name or url pattern name and cannot find one named &#x27;home&#x27;.<\/p>\n<p>From this error message we have the following initial debug steps:<\/p>\n<ol>\n<li>Check for views or urls named &#x27;home&#x27; or similar of such names.<\/li>\n<li>Search for the &#x27;home&#x27; string across the codebase to see what comes up.<\/li>\n<li>Review recent changes to view and urls.<\/li>\n<\/ol>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-66","title":"Reading Python Exception messages","summary":"Don't panic! Take a breath and read slowly","date_modified":"2024-04-10T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#error_messages","#reading"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-65","content_html":"<p>So far in my open source career, my biggest contribution has been helping others in the Django Discord and then becoming a moderator.\nI would like to broadern my involvment in open source, I have ideas around teaching others (these posts are a starting point for that).<\/p>\n<p>I think I would like to help maintain a package, but I am unsure of the initial steps to get started or even which package to even pick.\nFrom what I have read and listened to, a package I know and use regularly would be likely most suitable to me.\nBut that&#x27;s where I get a bit lost, although in just writing this out I realise I would need to set aside some regular time in a week to work on a project, be in mine or another.<\/p>\n<p>Any suggestions for which packages need some help?<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-65","title":"A programmer in search of a repository","summary":"I am looking to get more involved in being a maintainer","date_modified":"2024-04-09T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#packages","#maintainer"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-64","content_html":"<p>Over the weekend I was catching up with some reading of articles and one such page was the release notes of a neopoliton from Carlton where he introduced a <code>mktemplate<\/code> command.\nThis reminded me of a recent time where I shared <a href=\"\/100-words\/day-11\">day 11<\/a> with some on discord. This lead to the idea of a <code>cptemplate<\/code> command.<\/p>\n<p>The concept is fairly simple which is to make it easy to copy a template from Django or a third party package into your own project. Two examples of how it could work are as follows:<\/p>\n<pre class=\"language-shell\"><code class=\"language-shell\"><span class=\"token comment\"># Copy the admin\/base.html into the templates directory of my core django app<\/span>\npython manage.py cptemplate admin\/base.html core\n\n<span class=\"token comment\"># Copy the admin\/base.html into the &#x27;default&#x27; templates directory of the project<\/span>\npython manage.py cptemplate admin\/base.html\n<\/code><\/pre>\n<p>The second example obviously has an issue as the concept of a &#x27;default&#x27; templates directory doesn&#x27;t exist in Django.\nThis could feasibily be the first item in the <code>TEMPLATES[&quot;DIRS&quot;]<\/code> setting and to create a directory and dump the file if it doesn&#x27;t exist with instructions to add the directory to the setting.<\/p>\n<p>Let me know what you think about this one! Good, bad or indifferent?<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-64","title":"A new Django management command","summary":"A little helper to override templates","date_modified":"2024-04-08T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#templates","#packages"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-63","content_html":"<p>I cannot count the amount of times I have people asking about how to integrate Django and React and then struggling with various issues from CORS headers, authentication, static files and deployment to name just a few.\nThese issues are not too hard to deal with by themselves assuming you have a firm grip on Django itself, but typically it seems that these questions are asked by those doing their first or second project in Django.<\/p>\n<p>My advice is this, <strong>when learning a new technology, library or framework, learn it in isolation from other technologies and understand everything it provides before mixing in other technologies.<\/strong>\nIn the case of Django and React, Django provides HTML templating which while not being quite as in demand as React does provide a frontend without the huge complexities that come with using React.\nEssentially build a few sites using just Django and then start adding React if you need it.<\/p>\n<p>A final note, if you learnt React and want a fully functional framework to build your webapp, then I would first consider Next.js or Remix, both of these are a closer comparison to Django in what they offer out of the box but use React as a foundation\nwhich will lead to a simpler, cleaner developer experience overall.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-63","title":"You don't need React (or other frontend framework)","summary":"A mini rant about learning Django","date_modified":"2024-04-05T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#react","#frontend"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-62","content_html":"<p>As promised, here is the TestCase to test most model views in the apps you specify in the <code>local_apps<\/code> list.<\/p>\n<p>The test case uses subTests to check each add and changelist views, and it ought to be pretty simple to add the other view types as required.<\/p>\n<pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">from<\/span> django<span class=\"token punctuation\">.<\/span>contrib <span class=\"token keyword\">import<\/span> admin\n<span class=\"token keyword\">from<\/span> django<span class=\"token punctuation\">.<\/span>contrib<span class=\"token punctuation\">.<\/span>auth <span class=\"token keyword\">import<\/span> get_user_model\n<span class=\"token keyword\">from<\/span> django<span class=\"token punctuation\">.<\/span>test <span class=\"token keyword\">import<\/span> TestCase\n<span class=\"token keyword\">from<\/span> django<span class=\"token punctuation\">.<\/span>urls <span class=\"token keyword\">import<\/span> reverse\n<span class=\"token keyword\">from<\/span> django<span class=\"token punctuation\">.<\/span>utils<span class=\"token punctuation\">.<\/span>encoding <span class=\"token keyword\">import<\/span> force_text\n\n\n<span class=\"token keyword\">class<\/span> <span class=\"token class-name\">TestAdminViews<\/span><span class=\"token punctuation\">(<\/span>TestCase<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    <span class=\"token comment\"># this could be pulled from settings depending on how you decided<\/span>\n    <span class=\"token comment\"># to structure INSTALLED_APPS<\/span>\n    local_apps <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">[<\/span>\n        <span class=\"token string\">&#x27;core&#x27;<\/span><span class=\"token punctuation\">,<\/span>\n        <span class=\"token string\">&#x27;app1&#x27;<\/span>\n        <span class=\"token string\">&#x27;app2&#x27;<\/span>\n    <span class=\"token punctuation\">]<\/span>\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">test_admin_views<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        self<span class=\"token punctuation\">.<\/span>user <span class=\"token operator\">=<\/span> get_user_model<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span>create<span class=\"token punctuation\">(<\/span>username<span class=\"token operator\">=<\/span><span class=\"token string\">&#x27;test&#x27;<\/span><span class=\"token punctuation\">,<\/span> is_superuser<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">)<\/span>\n        self<span class=\"token punctuation\">.<\/span>user<span class=\"token punctuation\">.<\/span>set_password<span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;test&#x27;<\/span><span class=\"token punctuation\">)<\/span>\n        self<span class=\"token punctuation\">.<\/span>client<span class=\"token punctuation\">.<\/span>login<span class=\"token punctuation\">(<\/span>username<span class=\"token operator\">=<\/span>self<span class=\"token punctuation\">.<\/span>user<span class=\"token punctuation\">.<\/span>username<span class=\"token punctuation\">,<\/span> password<span class=\"token operator\">=<\/span><span class=\"token string\">&quot;test&quot;<\/span><span class=\"token punctuation\">)<\/span>\n\n        local_models <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">(<\/span>\n            k\n            <span class=\"token keyword\">for<\/span> k <span class=\"token keyword\">in<\/span> admin<span class=\"token punctuation\">.<\/span>site<span class=\"token punctuation\">.<\/span>_registry\n            <span class=\"token keyword\">if<\/span> k<span class=\"token punctuation\">.<\/span>__module__ <span class=\"token keyword\">in<\/span> self<span class=\"token punctuation\">.<\/span>local_apps\n        <span class=\"token punctuation\">)<\/span>\n        <span class=\"token keyword\">for<\/span> model <span class=\"token keyword\">in<\/span> local_models<span class=\"token punctuation\">:<\/span>\n            <span class=\"token keyword\">with<\/span> self<span class=\"token punctuation\">.<\/span>subTest<span class=\"token punctuation\">(<\/span>model<span class=\"token operator\">=<\/span>model<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n                self<span class=\"token punctuation\">.<\/span>admin_add_view<span class=\"token punctuation\">(<\/span>model<span class=\"token punctuation\">)<\/span>\n                self<span class=\"token punctuation\">.<\/span>admin_change_view<span class=\"token punctuation\">(<\/span>model<span class=\"token punctuation\">)<\/span>\n\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">admin_add_view<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">,<\/span> model<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        <span class=\"token keyword\">if<\/span> admin<span class=\"token punctuation\">.<\/span>site<span class=\"token punctuation\">.<\/span>_registry<span class=\"token punctuation\">[<\/span>model<span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">.<\/span>has_add_permission<span class=\"token punctuation\">(<\/span>\n            <span class=\"token builtin\">type<\/span><span class=\"token punctuation\">(<\/span><span class=\"token builtin\">str<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;request&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token punctuation\">{<\/span><span class=\"token string\">&quot;user&quot;<\/span><span class=\"token punctuation\">:<\/span> self<span class=\"token punctuation\">.<\/span>user<span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span>\n        <span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n            url <span class=\"token operator\">=<\/span> reverse<span class=\"token punctuation\">(<\/span>\n                <span class=\"token string\">&quot;admin:{0}_{1}_add&quot;<\/span><span class=\"token punctuation\">.<\/span><span class=\"token builtin\">format<\/span><span class=\"token punctuation\">(<\/span>\n                    model<span class=\"token punctuation\">.<\/span>_meta<span class=\"token punctuation\">.<\/span>app_label<span class=\"token punctuation\">,<\/span> model<span class=\"token punctuation\">.<\/span>_meta<span class=\"token punctuation\">.<\/span>model_name\n                <span class=\"token punctuation\">)<\/span>\n            <span class=\"token punctuation\">)<\/span>\n            self<span class=\"token punctuation\">.<\/span>assert_admin_valid_view<span class=\"token punctuation\">(<\/span>model<span class=\"token punctuation\">,<\/span> url<span class=\"token punctuation\">)<\/span>\n\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">admin_change_view<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">,<\/span> model<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        url <span class=\"token operator\">=<\/span> self<span class=\"token punctuation\">.<\/span>reverse<span class=\"token punctuation\">(<\/span>\n            <span class=\"token string\">&quot;admin:{0!s}_{1!s}_changelist&quot;<\/span><span class=\"token punctuation\">.<\/span><span class=\"token builtin\">format<\/span><span class=\"token punctuation\">(<\/span>\n                model<span class=\"token punctuation\">.<\/span>_meta<span class=\"token punctuation\">.<\/span>app_label<span class=\"token punctuation\">,<\/span> model<span class=\"token punctuation\">.<\/span>_meta<span class=\"token punctuation\">.<\/span>model_name\n            <span class=\"token punctuation\">)<\/span>\n        <span class=\"token punctuation\">)<\/span>\n        self<span class=\"token punctuation\">.<\/span>assert_admin_valid_view<span class=\"token punctuation\">(<\/span>model<span class=\"token punctuation\">,<\/span> url<span class=\"token punctuation\">)<\/span>\n\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">assert_admin_valid_view<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">,<\/span> model<span class=\"token punctuation\">,<\/span> url<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        response <span class=\"token operator\">=<\/span> self<span class=\"token punctuation\">.<\/span>client<span class=\"token punctuation\">.<\/span>get<span class=\"token punctuation\">(<\/span>url<span class=\"token punctuation\">,<\/span> follow<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">)<\/span>\n        self<span class=\"token punctuation\">.<\/span>assertEqual<span class=\"token punctuation\">(<\/span>\n            response<span class=\"token punctuation\">.<\/span>status_code<span class=\"token punctuation\">,<\/span>\n            <span class=\"token number\">200<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;{0!s} != {1!s} -&gt; {2!s}, url: {3!s}&quot;<\/span><span class=\"token punctuation\">.<\/span><span class=\"token builtin\">format<\/span><span class=\"token punctuation\">(<\/span>\n                response<span class=\"token punctuation\">.<\/span>status_code<span class=\"token punctuation\">,<\/span> <span class=\"token number\">200<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token builtin\">repr<\/span><span class=\"token punctuation\">(<\/span>model<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span> url\n            <span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n        <span class=\"token punctuation\">)<\/span>\n        self<span class=\"token punctuation\">.<\/span>assertFalse<span class=\"token punctuation\">(<\/span>\n            <span class=\"token string\">&quot;this_is_the_login_form&quot;<\/span> <span class=\"token keyword\">in<\/span> force_text<span class=\"token punctuation\">(<\/span>response<span class=\"token punctuation\">.<\/span>content<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n            <span class=\"token string\">&quot;login requested for {0!s}&quot;<\/span><span class=\"token punctuation\">.<\/span><span class=\"token builtin\">format<\/span><span class=\"token punctuation\">(<\/span>force_text<span class=\"token punctuation\">(<\/span>model<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n        <span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>A quick breakdown of how this TestCase works, there are three types of methods in this class:<\/p>\n<p>First we have the actual test method (<code>test_admin_views<\/code>). This iterates over each model and using subTest tests each view.\nNext we have a method for each view. These methods deal with generating the correct url then passing the model and url to the final method type.\nThis final method is an assert helper which makes the request with the client and then asserts the response.<\/p>\n<p>While preparing this post, there are likely some improvements to be made, but this is an excellent starting point to prevent admin misconfigurations getting into production.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-62","title":"Testing the admin - part 2","summary":"This test case with check most of your admin views","date_modified":"2024-04-04T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#admin","#tests"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-61","content_html":"<p>When using a framework like Django, there is a trap you can fall into of writing tests that the framework will have already covered. A good example of this is form rendering, if you have the following form:<\/p>\n<pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">class<\/span> <span class=\"token class-name\">MyForm<\/span><span class=\"token punctuation\">(<\/span>forms<span class=\"token punctuation\">.<\/span>Form<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n  title <span class=\"token operator\">=<\/span> forms<span class=\"token punctuation\">.<\/span>CharField<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n  description <span class=\"token operator\">=<\/span> forms<span class=\"token punctuation\">.<\/span>CharField<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>that gets rendered in a template with <code>{{ form }}<\/code> or <code>{{ form.as_p }}<\/code>.\nNow it would seem logical to have a test that checks that the form has rendered correctly, but this would just be a duplicate of a test somewhere in Django that already exists. This is the trap!<\/p>\n<p>Testing any custom logic within the form would be suitable, but not the default rendering.<\/p>\n<p>I have found one exception to this in a few codebases I have worked on over the years. It can be sensible to test all the admin views from your codebase.\nYes, Django will have some tests, but the admin can easily break with some misconfiguration which some view tests can catch.<\/p>\n<p>Tomorrow I will provide those tests for you to dump into your own project if you so desire!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-61","title":"Testing the admin","summary":"One exception to the normal rule","date_modified":"2024-04-03T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#admin","#tests"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-60","content_html":"<p>When it comes to testing Django, the easiest part to test are your views as this is the entry point to most of the custom business logic that isn&#x27;t covered by Django itself.<\/p>\n<p>The structure of a view test ought to follow this relatively simple pattern:<\/p>\n<ol>\n<li>Setup the required data<\/li>\n<li>Assert the initial state of the system<\/li>\n<li>Construct the data to be sent to the view (if required)<\/li>\n<li>Make a call to the view via the Django client<\/li>\n<li>Assert the response is as expected (HTTP status code, any content)<\/li>\n<li>Assert the final state of the system as a result of the view logic.<\/li>\n<\/ol>\n<p>Some important notes about this pattern:<\/p>\n<ul>\n<li>The complexity that is hidden in the in the setup of the data. This depends hugely on the complexity of the data model.<\/li>\n<li>The assertions before making any changes are key to demonstrate that it is a single test making the expected changes and not some other test making the changes. Tests need to work in isolation from each other.<\/li>\n<li>Tests should be DRY - <strong>DO<\/strong> Repeat Yourself, it is fine to copy\/paste a test then change a single line to cover a different case.<\/li>\n<li>Related to the last point keep the tests simple and obvious, don&#x27;t try to do fancy logic by deriving data from model data, just type it out.<\/li>\n<li>Finally don&#x27;t use Django fixtures, but generate the models dynamically. Other options here include pytest fixtures or factory_boy<\/li>\n<\/ul>\n<p>Finallly a quick example of some pseudo code:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\">\n<span class=\"token keyword\">class<\/span> <span class=\"token class-name\">MyTestCase<\/span><span class=\"token punctuation\">(<\/span>TestCase<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n\n  <span class=\"token keyword\">def<\/span> <span class=\"token function\">my_view_test<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    <span class=\"token comment\"># Step 1<\/span>\n    user <span class=\"token operator\">=<\/span> User<span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span>create<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">)<\/span>\n    profile <span class=\"token operator\">=<\/span> Profile<span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span>create<span class=\"token punctuation\">(<\/span>user<span class=\"token operator\">=<\/span>user<span class=\"token punctuation\">,<\/span> <span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">)<\/span>\n    <span class=\"token comment\"># Step 2<\/span>\n    self<span class=\"token punctuation\">.<\/span>assertEqual<span class=\"token punctuation\">(<\/span>profile<span class=\"token punctuation\">.<\/span>age<span class=\"token punctuation\">,<\/span> <span class=\"token boolean\">None<\/span><span class=\"token punctuation\">)<\/span>\n    self<span class=\"token punctuation\">.<\/span>assertEqual<span class=\"token punctuation\">(<\/span>profile<span class=\"token punctuation\">.<\/span>twitter<span class=\"token punctuation\">,<\/span> <span class=\"token string\">&#x27;&#x27;<\/span><span class=\"token punctuation\">)<\/span>\n\n    <span class=\"token comment\"># Step 3<\/span>\n    data <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">{<\/span>\n      <span class=\"token string\">&#x27;age&#x27;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token number\">23<\/span><span class=\"token punctuation\">,<\/span>\n      <span class=\"token string\">&#x27;bio&#x27;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">&#x27;I like to build things&#x27;<\/span>\n      <span class=\"token string\">&#x27;twitter&#x27;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">&#x27;nanorepublica&#x27;<\/span>\n    <span class=\"token punctuation\">}<\/span>\n\n    <span class=\"token comment\"># Step 4<\/span>\n    url <span class=\"token operator\">=<\/span> reverse<span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;profile-update&#x27;<\/span><span class=\"token punctuation\">)<\/span>\n    resp <span class=\"token operator\">=<\/span> self<span class=\"token punctuation\">.<\/span>client<span class=\"token punctuation\">.<\/span>post<span class=\"token punctuation\">(<\/span>url<span class=\"token punctuation\">,<\/span> data<span class=\"token punctuation\">)<\/span>\n\n    <span class=\"token comment\"># Step 5<\/span>\n    self<span class=\"token punctuation\">.<\/span>assertRedirects<span class=\"token punctuation\">(<\/span>resp<span class=\"token punctuation\">,<\/span> <span class=\"token string\">&#x27;\/profile&#x27;<\/span><span class=\"token punctuation\">)<\/span>\n\n    <span class=\"token comment\"># Step 6<\/span>\n    self<span class=\"token punctuation\">.<\/span>assertEqual<span class=\"token punctuation\">(<\/span>profile<span class=\"token punctuation\">.<\/span>age<span class=\"token punctuation\">,<\/span> <span class=\"token number\">23<\/span><span class=\"token punctuation\">)<\/span>\n    self<span class=\"token punctuation\">.<\/span>assertEqual<span class=\"token punctuation\">(<\/span>profile<span class=\"token punctuation\">.<\/span>twitter<span class=\"token punctuation\">,<\/span> <span class=\"token string\">&#x27;nanorepublica&#x27;<\/span><span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-60","title":"A blueprint for django tests","summary":"Honestly this what most of the first tests you write should look like","date_modified":"2024-04-02T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#views","#tests"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-59","content_html":"<p>We are revisiting static and media files as I realised the last post did not quite finish the subject well enough!<\/p>\n<p>When deploying a project to a production environment, the main alteration in settings will switching the storage backend that will be used so Django understands what to do with media and static files.<\/p>\n<p>For example when configuring Whitenoise the following storage backend needs to used:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\">STORAGES <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">{<\/span>\n    <span class=\"token comment\"># ...<\/span>\n    <span class=\"token string\">&quot;staticfiles&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token punctuation\">{<\/span>\n        <span class=\"token string\">&quot;BACKEND&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">&quot;whitenoise.storage.CompressedManifestStaticFilesStorage&quot;<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">,<\/span>\n<span class=\"token punctuation\">}<\/span>\n<\/code><\/pre>\n<p>Django Storages has a number of different options depending on the cloud provider you choose to use.<\/p>\n<p>But what exactly is a storage backend? A storage backend provides a common API for Django to process and serve static and media files correctly depending on where they are stored.\nThis included copying static files and media files to the correct place, handling the resolution of static tag to the correct url and the resolution or the <code>.url<\/code> attribute of FileFields and ImageFields.<\/p>\n<p>Storage backends allow Django to support an infinite possible way of handling files without having to know about every type of situation within the core of Django itself.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-59","title":"Storage Backends - what are they?","summary":"Changing how static and media files are handled in Django","date_modified":"2024-04-01T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#static_files","#django","#media_files","#storage","#backends"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-58","content_html":"<p>Today is a part 2 of naming conventions from a while back. Disclaimer I may have covered this in another post! Anyway onward!<\/p>\n<p>Django has three types of relations (ForeignKey&#x27;s, ManytoMany and OneToOne Fields). Each of these should named appropriately to ensure the code makes logical sense and it maintainable over the long haul.<\/p>\n<p>ForeignKey&#x27;s should be named as a singular. The reverse relation, if specified via <code>related_name<\/code> should be a plural or have the word <code>set<\/code> used in it (this is the defualt).\nExample: A book model would have have a single author, but the reverse would by default be <code>book_set<\/code> or could be <code>authored_books<\/code><\/p>\n<p>A ManytoMany should be named as a plural for both the forward and reverse relation. Example: A user could be in multiple groups and a groups can have multiple users, so the names of <code>users<\/code> and <code>groups<\/code> would be most appropriate.<\/p>\n<p>Finally a OneToOne should be singular both ways. Example: A User has a single profile and the profile has a single user.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-58","title":"Django relations naming conventions","summary":"How to name them so they make sense in your code","date_modified":"2024-03-29T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#naming","#django"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-57","content_html":"<p>When considering static and media files in the context of deployment there are a few options available depending on where you are choosing to deploy.<\/p>\n<p>An excellent start for static files is to use the <a href=\"https:\/\/whitenoise.readthedocs.io\/en\/latest\/#\">Whitenoise<\/a> package which handles pretty much everything you need.<\/p>\n<p>The other more complex options include the following:<\/p>\n<ol>\n<li>If you are using a plain linux VPS or similar, then you can configuring nginx\/apache\/caddy to serve the static files<\/li>\n<li>You can use django-storages to configure object storage like S3 or Cloudflare R2. This is essential if you are using a PaaS such as render, heroku or fly otherwise you will lose data at some point.<\/li>\n<\/ol>\n<p>When it comes to media files unfortunately there isn&#x27;t a similar whitenoise package available, so the most common option these days is picking one of the solutions just mentioned.\nObject Storage is slightly easier to get started with if you don&#x27;t want to be messing around with web server configuration, but this can depend on the existing setup.<\/p>\n<p>If you need help with this then I am available for hire!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-57","title":"Static and Media when deployed","summary":"How do settings change when we deploy to production?","date_modified":"2024-03-28T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#media_files","#static_files","#deployment"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-56","content_html":"<p>Today is turn of media file settings and dealing with file uploads in general. First, the settings required for development are as follows:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\">MEDIA_URL <span class=\"token operator\">=<\/span> <span class=\"token string\">&#x27;media\/&#x27;<\/span>\nMEDIA_ROOT <span class=\"token operator\">=<\/span> BASE_DIR <span class=\"token operator\">\/<\/span> <span class=\"token string\">&#x27;media_root&#x27;<\/span>\n<\/code><\/pre>\n<p><code>MEDIA_URL<\/code> defines the public facing url for where media files are served from.\n<code>MEDIA_ROOT<\/code> defines where uploaded files are stored within the context of the project, for local development a folder in the project is an excellent start.<\/p>\n<p>Finally when it comes to uploading files there are a few things to note.<\/p>\n<ol>\n<li>Using a FileField or ImageField are default ways to save files into a model.<\/li>\n<li>If your using a Django Form, then be sure to pass in <code>request.FILES<\/code> as well as <code>request.POST<\/code> when instaniating the form<\/li>\n<li>When constructing the HTML form in a template be sure to mark the <code>enctype<\/code> as <code>&quot;multipart\/form-data&quot;<\/code>. See the example below.<\/li>\n<\/ol>\n<pre class=\"language-html\"><code class=\"language-html\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>form<\/span> <span class=\"token attr-name\">method<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>POST<span class=\"token punctuation\">&quot;<\/span><\/span> <span class=\"token attr-name\">enctype<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>multipart\/form-data<span class=\"token punctuation\">&quot;<\/span><\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n    {% csrf_token %}\n    {{ form }}\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;<\/span>input<\/span> <span class=\"token attr-name\">type<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>submit<span class=\"token punctuation\">&quot;<\/span><\/span> <span class=\"token attr-name\">value<\/span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=<\/span><span class=\"token punctuation\">&quot;<\/span>Upload File<span class=\"token punctuation\">&quot;<\/span><\/span> <span class=\"token punctuation\">\/&gt;<\/span><\/span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;\/<\/span>form<\/span><span class=\"token punctuation\">&gt;<\/span><\/span>\n<\/code><\/pre>\n<p>Tomorrow we will look at static files and media files when deploying a Django project.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-56","title":"Media files - Settings","summary":"Doing File uploads","date_modified":"2024-03-27T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#media_files"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-55","content_html":"<p>Today we will cover what&#x27;s required to get static files to actually work in development.<\/p>\n<p>The default Django setup provides just the initial setting of <code>STATIC_URL = &#x27;static\/&#x27;<\/code>.\nThis is a good start and it would appear that this is only provided since these other settings make assumptions about the project structure.<\/p>\n<p>There are a minimum of two other changes to your settings for static files to work, first add <code>STATIC_ROOT<\/code>. This should be a directory relative to your <code>BASE_DIR<\/code>, for example:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\">STATIC_ROOT <span class=\"token operator\">=<\/span> BASE_DIR <span class=\"token operator\">\/<\/span> <span class=\"token string\">&#x27;static_root&#x27;<\/span>\n<\/code><\/pre>\n<p>The <code>static_root<\/code> directory needs to exist and added to your <code>.gitignore<\/code> file. This is where all static files will be copied to when <code>manage.py collectstatic<\/code> is executed.<\/p>\n<p>Finally we need either ensure our Django app is listed in <code>INSTALLED_APPS<\/code> for <code>collectstatic<\/code> to pick up files within an app or we need to use <code>STATICFILES_DIRS<\/code> which is a list of directories outside of the Django app structure where static files reside.<\/p>\n<p>The final complete settigs is as follows:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\">STATIC_URL <span class=\"token operator\">=<\/span> <span class=\"token string\">&#x27;static\/&#x27;<\/span>\nSTATIC_ROOT <span class=\"token operator\">=<\/span> BASE_DIR <span class=\"token operator\">\/<\/span> <span class=\"token string\">&#x27;static_root&#x27;<\/span>\nSTATICFILES_DIRS <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">[<\/span>\n    BASE_DIR <span class=\"token operator\">\/<\/span> <span class=\"token string\">&#x27;my_project&#x27;<\/span> <span class=\"token operator\">\/<\/span> <span class=\"token string\">&#x27;static&#x27;<\/span>\n<span class=\"token punctuation\">]<\/span>\n<\/code><\/pre>\n<p>which has the following file structure:<\/p>\n<pre class=\"language-shell\"><code class=\"language-shell\">\u251c\u2500\u2500 db.sqlite3\n\u251c\u2500\u2500 manage.py\n\u251c\u2500\u2500 static_root\n\u2514\u2500\u2500 my_project\n    \u251c\u2500\u2500 __init__.py\n    \u251c\u2500\u2500 asgi.py\n    \u251c\u2500\u2500 settings.py\n    \u251c\u2500\u2500 static\n    \u2502\u00a0\u00a0 \u2514\u2500\u2500 style.css\n    \u251c\u2500\u2500 templates\n    \u2502\u00a0\u00a0 \u2514\u2500\u2500 index.html\n    \u251c\u2500\u2500 urls.py\n    \u2514\u2500\u2500 wsgi.py\n<\/code><\/pre>\n<p>Tomorrow we will expand on this for media files.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-55","title":"Static files - Settings","summary":"What's required to get them to actually work","date_modified":"2024-03-26T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#static_files"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-54","content_html":"<p>Static files and Media files is another common issue that newcomers to Django often struggle with, be it the concepts themselves, the settings configuration and how to manage them in deployed environment.\nToday, let&#x27;s just focus on the concepts.<\/p>\n<p>Static files are typically files to do with rendering the frontend of the site, be it images, favicons, CSS or Javascript files.\nAs a developer you want to commit them to the repository either at a the project level or within each Django app. They only change at during development and updated in production during a release.<\/p>\n<p>Media files are user generated files. Users include administrators, staff or end-users of the site. The are more typically images, documents or other data files.\nThey do not exist within the repository and are entirely optional if your site doesn&#x27;t require user-generated files. They can only be changed once a site is running and do not tend to replicate between environments.<\/p>\n<p>Tomorrow we will look at what a settings file should look like for static files<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-54","title":"Static and media files","summary":"What are they and when do I need them?","date_modified":"2024-03-25T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#static_files","#media_files"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-53","content_html":"<p>Before starting to use viewsets, I would recommend becoming familiar with class based views, as they are similar in structure in that they are a python class rather than a function.<\/p>\n<p>With that aside out of the way, you might guess from the name, that a viewset is a set of views.\nThey are used to group related views for a resource, namely the common CRUD actions for a resource using predefined urls defined by a DRF Router.<\/p>\n<p>A viewset expects methods to be defined either via inheritance as is the case of the ModelViewSet or directly on the viewset itself. These methods are be <code>list<\/code>, <code>retrieve<\/code>, <code>create<\/code>, <code>update<\/code> and <code>destory<\/code>.<\/p>\n<p>A final note is that viewsets are designed to reduce the amount of boilerplate code required to wire up urls to views to serializers and ought to be used only once your comfortable with them and that logic involved is trivial.\nFor complex individual APIs stick to simpler class based or function based views<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-53","title":"DRF Viewsets in 100 words","summary":"What exactly is a viewset?","date_modified":"2024-03-22T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#rest_framework"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-52","content_html":"<p>Yesterday we finished we the following class-based view:<\/p>\n<pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">class<\/span> <span class=\"token class-name\">GetNameView<\/span><span class=\"token punctuation\">(<\/span>View<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">get<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">,<\/span> request<span class=\"token punctuation\">,<\/span> <span class=\"token operator\">*<\/span>args<span class=\"token punctuation\">,<\/span> <span class=\"token operator\">**<\/span>kwargs<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        form <span class=\"token operator\">=<\/span> NameForm<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n        <span class=\"token keyword\">return<\/span> render<span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">,<\/span> <span class=\"token string\">&quot;name.html&quot;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token punctuation\">{<\/span><span class=\"token string\">&quot;form&quot;<\/span><span class=\"token punctuation\">:<\/span> form<span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span>\n\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">post<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">,<\/span> request<span class=\"token punctuation\">,<\/span> <span class=\"token operator\">*<\/span>args<span class=\"token punctuation\">,<\/span> <span class=\"token operator\">**<\/span>kwargs<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        form <span class=\"token operator\">=<\/span> NameForm<span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">.<\/span>POST<span class=\"token punctuation\">)<\/span>\n        <span class=\"token comment\"># check whether it&#x27;s valid:<\/span>\n        <span class=\"token keyword\">if<\/span> form<span class=\"token punctuation\">.<\/span>is_valid<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n            <span class=\"token comment\"># process the data in form.cleaned_data as required<\/span>\n            form<span class=\"token punctuation\">.<\/span>save<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n            <span class=\"token comment\"># redirect to a new URL:<\/span>\n            <span class=\"token keyword\">return<\/span> HttpResponseRedirect<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;\/thanks\/&quot;<\/span><span class=\"token punctuation\">)<\/span>\n        <span class=\"token keyword\">return<\/span> render<span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">,<\/span> <span class=\"token string\">&quot;name.html&quot;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token punctuation\">{<\/span><span class=\"token string\">&quot;form&quot;<\/span><span class=\"token punctuation\">:<\/span> form<span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>However, there is some minor repeatition and it&#x27;s not easy to reuse the functionality in multiple views. Let&#x27;s fix this!<\/p>\n<p>First up, is the render call is happening in both methods so lets refactor that into it&#x27;s own function:<\/p>\n<pre class=\"language-python\"><code class=\"language-python\">    template_name <span class=\"token operator\">=<\/span> <span class=\"token string\">&quot;name.html&quot;<\/span>\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">render_to_response<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">,<\/span> context<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        <span class=\"token keyword\">return<\/span> render<span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">,<\/span> self<span class=\"token punctuation\">.<\/span>template_name<span class=\"token punctuation\">,<\/span> context<span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>While we were here we passed in the context as a parameter and made so the template could easily be switched around.<\/p>\n<p>Next on the list would be to handle the instiation of the form since this is mostly common across both methods:<\/p>\n<pre class=\"language-python\"><code class=\"language-python\">    form_class <span class=\"token operator\">=<\/span> NameForm\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">get_form_kwargs<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        kwargs <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">{<\/span><span class=\"token punctuation\">}<\/span>\n        <span class=\"token keyword\">if<\/span> self<span class=\"token punctuation\">.<\/span>request<span class=\"token punctuation\">.<\/span>method <span class=\"token keyword\">in<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;POST&quot;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token string\">&quot;PUT&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n            kwargs<span class=\"token punctuation\">.<\/span>update<span class=\"token punctuation\">(<\/span>\n                <span class=\"token punctuation\">{<\/span>\n                    <span class=\"token string\">&quot;data&quot;<\/span><span class=\"token punctuation\">:<\/span> self<span class=\"token punctuation\">.<\/span>request<span class=\"token punctuation\">.<\/span>POST<span class=\"token punctuation\">,<\/span>\n                <span class=\"token punctuation\">}<\/span>\n            <span class=\"token punctuation\">)<\/span>\n        <span class=\"token keyword\">return<\/span> kwargs\n\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">get_form<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        <span class=\"token keyword\">return<\/span> self<span class=\"token punctuation\">.<\/span>form_class<span class=\"token punctuation\">(<\/span><span class=\"token operator\">**<\/span>self<span class=\"token punctuation\">.<\/span>get_form_kwargs<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>Finally the redirect could be better handled as a class attribute:<\/p>\n<pre class=\"language-python\"><code class=\"language-python\">    success_url <span class=\"token operator\">=<\/span> <span class=\"token string\">&quot;\/thanks\/&quot;<\/span>\n<\/code><\/pre>\n<p>All this comes together as follows:<\/p>\n<pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">class<\/span> <span class=\"token class-name\">GetNameView<\/span><span class=\"token punctuation\">(<\/span>View<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    template_name <span class=\"token operator\">=<\/span> <span class=\"token string\">&quot;name.html&quot;<\/span>\n    success_url <span class=\"token operator\">=<\/span> <span class=\"token string\">&quot;\/thanks\/&quot;<\/span>\n    form_class <span class=\"token operator\">=<\/span> NameForm\n\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">get_form_kwargs<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        kwargs <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">{<\/span><span class=\"token punctuation\">}<\/span>\n        <span class=\"token keyword\">if<\/span> self<span class=\"token punctuation\">.<\/span>request<span class=\"token punctuation\">.<\/span>method <span class=\"token keyword\">in<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;POST&quot;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token string\">&quot;PUT&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n            kwargs<span class=\"token punctuation\">.<\/span>update<span class=\"token punctuation\">(<\/span>\n                <span class=\"token punctuation\">{<\/span>\n                    <span class=\"token string\">&quot;data&quot;<\/span><span class=\"token punctuation\">:<\/span> self<span class=\"token punctuation\">.<\/span>request<span class=\"token punctuation\">.<\/span>POST<span class=\"token punctuation\">,<\/span>\n                <span class=\"token punctuation\">}<\/span>\n            <span class=\"token punctuation\">)<\/span>\n        <span class=\"token keyword\">return<\/span> kwargs\n\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">get_form<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        <span class=\"token keyword\">return<\/span> self<span class=\"token punctuation\">.<\/span>form_class<span class=\"token punctuation\">(<\/span><span class=\"token operator\">**<\/span>self<span class=\"token punctuation\">.<\/span>get_form_kwargs<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">)<\/span>\n\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">render_to_response<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">,<\/span> context<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        <span class=\"token keyword\">return<\/span> render<span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">,<\/span> self<span class=\"token punctuation\">.<\/span>template_name<span class=\"token punctuation\">,<\/span> context<span class=\"token punctuation\">)<\/span>\n\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">get<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">,<\/span> request<span class=\"token punctuation\">,<\/span> <span class=\"token operator\">*<\/span>args<span class=\"token punctuation\">,<\/span> <span class=\"token operator\">**<\/span>kwargs<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        form <span class=\"token operator\">=<\/span> self<span class=\"token punctuation\">.<\/span>get_form<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n        <span class=\"token keyword\">return<\/span> self<span class=\"token punctuation\">.<\/span>render_to_response<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">{<\/span><span class=\"token string\">&quot;form&quot;<\/span><span class=\"token punctuation\">:<\/span> form<span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span>\n\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">post<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">,<\/span> request<span class=\"token punctuation\">,<\/span> <span class=\"token operator\">*<\/span>args<span class=\"token punctuation\">,<\/span> <span class=\"token operator\">**<\/span>kwargs<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        form <span class=\"token operator\">=<\/span> self<span class=\"token punctuation\">.<\/span>get_form<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n        <span class=\"token comment\"># check whether it&#x27;s valid:<\/span>\n        <span class=\"token keyword\">if<\/span> form<span class=\"token punctuation\">.<\/span>is_valid<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n            <span class=\"token comment\"># process the data in form.cleaned_data as required<\/span>\n            form<span class=\"token punctuation\">.<\/span>save<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n            <span class=\"token comment\"># redirect to a new URL:<\/span>\n            <span class=\"token keyword\">return<\/span> HttpResponseRedirect<span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">.<\/span>success_url<span class=\"token punctuation\">)<\/span>\n        <span class=\"token keyword\">return<\/span> self<span class=\"token punctuation\">.<\/span>render_to_response<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">{<\/span><span class=\"token string\">&quot;form&quot;<\/span><span class=\"token punctuation\">:<\/span> form<span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>While this is much more code it can now be split into two classes as follows:<\/p>\n<pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">class<\/span> <span class=\"token class-name\">MyFormView<\/span><span class=\"token punctuation\">(<\/span>View<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">get_form_kwargs<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        kwargs <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">{<\/span><span class=\"token punctuation\">}<\/span>\n        <span class=\"token keyword\">if<\/span> self<span class=\"token punctuation\">.<\/span>request<span class=\"token punctuation\">.<\/span>method <span class=\"token keyword\">in<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;POST&quot;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token string\">&quot;PUT&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n            kwargs<span class=\"token punctuation\">.<\/span>update<span class=\"token punctuation\">(<\/span>\n                <span class=\"token punctuation\">{<\/span>\n                    <span class=\"token string\">&quot;data&quot;<\/span><span class=\"token punctuation\">:<\/span> self<span class=\"token punctuation\">.<\/span>request<span class=\"token punctuation\">.<\/span>POST<span class=\"token punctuation\">,<\/span>\n                <span class=\"token punctuation\">}<\/span>\n            <span class=\"token punctuation\">)<\/span>\n        <span class=\"token keyword\">return<\/span> kwargs\n\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">get_form<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        <span class=\"token keyword\">return<\/span> self<span class=\"token punctuation\">.<\/span>form_class<span class=\"token punctuation\">(<\/span><span class=\"token operator\">**<\/span>self<span class=\"token punctuation\">.<\/span>get_form_kwargs<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">)<\/span>\n\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">render_to_response<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">,<\/span> context<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        <span class=\"token keyword\">return<\/span> render<span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">,<\/span> self<span class=\"token punctuation\">.<\/span>template_name<span class=\"token punctuation\">,<\/span> context<span class=\"token punctuation\">)<\/span>\n\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">get<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">,<\/span> request<span class=\"token punctuation\">,<\/span> <span class=\"token operator\">*<\/span>args<span class=\"token punctuation\">,<\/span> <span class=\"token operator\">**<\/span>kwargs<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        form <span class=\"token operator\">=<\/span> self<span class=\"token punctuation\">.<\/span>get_form<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n        <span class=\"token keyword\">return<\/span> self<span class=\"token punctuation\">.<\/span>render_to_response<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">{<\/span><span class=\"token string\">&quot;form&quot;<\/span><span class=\"token punctuation\">:<\/span> form<span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span>\n\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">post<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">,<\/span> request<span class=\"token punctuation\">,<\/span> <span class=\"token operator\">*<\/span>args<span class=\"token punctuation\">,<\/span> <span class=\"token operator\">**<\/span>kwargs<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        form <span class=\"token operator\">=<\/span> self<span class=\"token punctuation\">.<\/span>get_form<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n        <span class=\"token comment\"># check whether it&#x27;s valid:<\/span>\n        <span class=\"token keyword\">if<\/span> form<span class=\"token punctuation\">.<\/span>is_valid<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n            <span class=\"token comment\"># process the data in form.cleaned_data as required<\/span>\n            form<span class=\"token punctuation\">.<\/span>save<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n            <span class=\"token comment\"># redirect to a new URL:<\/span>\n            <span class=\"token keyword\">return<\/span> HttpResponseRedirect<span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">.<\/span>success_url<span class=\"token punctuation\">)<\/span>\n        <span class=\"token keyword\">return<\/span> self<span class=\"token punctuation\">.<\/span>render_to_response<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">{<\/span><span class=\"token string\">&quot;form&quot;<\/span><span class=\"token punctuation\">:<\/span> form<span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span>\n\n<span class=\"token keyword\">class<\/span> <span class=\"token class-name\">GetNameView<\/span><span class=\"token punctuation\">(<\/span>MyFormView<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    template_name <span class=\"token operator\">=<\/span> <span class=\"token string\">&quot;name.html&quot;<\/span>\n    success_url <span class=\"token operator\">=<\/span> <span class=\"token string\">&quot;\/thanks\/&quot;<\/span>\n    form_class <span class=\"token operator\">=<\/span> NameForm\n<\/code><\/pre>\n<p>The original view is not much more condensed and the base Form class (MyFormView) can be reused again with minor modifications.\nHowever at this point I would recommend having a look at the <code>FormView<\/code> in Django at this point as the above generic class view is a simplified version of it!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-52","title":"Moving towards Generic Class based views","summary":"Cleaning up a class based view","date_modified":"2024-03-21T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#views"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-51","content_html":"<p>Whatever your opinion on which style of view is &#x27;better&#x27;, newcomers can get confused by class based views (CBV) and there close relatives generic class based views (GCBV).\nThe typical pattern is jumping straight to the generic views without understanding what is going on and why there are certain methods or attributes to override.<\/p>\n<p>Today we are going to take a step back and just consider transistioning from a class function view to the most basic class based view.<\/p>\n<p>Below is a very typical function based view, which deals with rendering and processing a form depending on whether is HTTP method is a GET or POST.<\/p>\n<pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">def<\/span> <span class=\"token function\">get_name<\/span><span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    <span class=\"token comment\"># if this is a POST request we need to process the form data<\/span>\n    <span class=\"token keyword\">if<\/span> request<span class=\"token punctuation\">.<\/span>method <span class=\"token operator\">==<\/span> <span class=\"token string\">&quot;POST&quot;<\/span><span class=\"token punctuation\">:<\/span>\n        <span class=\"token comment\"># create a form instance and populate it with data from the request:<\/span>\n        form <span class=\"token operator\">=<\/span> NameForm<span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">.<\/span>POST<span class=\"token punctuation\">)<\/span>\n        <span class=\"token comment\"># check whether it&#x27;s valid:<\/span>\n        <span class=\"token keyword\">if<\/span> form<span class=\"token punctuation\">.<\/span>is_valid<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n            <span class=\"token comment\"># process the data in form.cleaned_data as required<\/span>\n            form<span class=\"token punctuation\">.<\/span>save<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n            <span class=\"token comment\"># redirect to a new URL:<\/span>\n            <span class=\"token keyword\">return<\/span> HttpResponseRedirect<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;\/thanks\/&quot;<\/span><span class=\"token punctuation\">)<\/span>\n\n    <span class=\"token comment\"># if a GET (or any other method) we&#x27;ll create a blank form<\/span>\n    <span class=\"token keyword\">else<\/span><span class=\"token punctuation\">:<\/span>\n        form <span class=\"token operator\">=<\/span> NameForm<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n\n    <span class=\"token keyword\">return<\/span> render<span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">,<\/span> <span class=\"token string\">&quot;name.html&quot;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token punctuation\">{<\/span><span class=\"token string\">&quot;form&quot;<\/span><span class=\"token punctuation\">:<\/span> form<span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>In a class based view the methods to typically interact with are methods that represent the HTTP method, so from above we have GET and POST which correspond to <code>def get(...)<\/code> and <code>def post(...)<\/code> respectively.\nThis means that any code not indented inside a conditional that checks the request method should live in both class methods, and any code that is located within the conditional needs to be moved into their respective class methods.<\/p>\n<p>So in our example above the following line needs to be migrated to both methods:<\/p>\n<pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">return<\/span> render<span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">,<\/span> <span class=\"token string\">&quot;name.html&quot;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token punctuation\">{<\/span><span class=\"token string\">&quot;form&quot;<\/span><span class=\"token punctuation\">:<\/span> form<span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>Then the following lines are within the POST conditional so should be moved to the <code>post<\/code> method:<\/p>\n<pre class=\"language-python\"><code class=\"language-python\">form <span class=\"token operator\">=<\/span> NameForm<span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">.<\/span>POST<span class=\"token punctuation\">)<\/span>\n<span class=\"token comment\"># check whether it&#x27;s valid:<\/span>\n<span class=\"token keyword\">if<\/span> form<span class=\"token punctuation\">.<\/span>is_valid<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    <span class=\"token comment\"># process the data in form.cleaned_data as required<\/span>\n    form<span class=\"token punctuation\">.<\/span>save<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n    <span class=\"token comment\"># redirect to a new URL:<\/span>\n    <span class=\"token keyword\">return<\/span> HttpResponseRedirect<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;\/thanks\/&quot;<\/span><span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>and finally the following code needs to be moved to the <code>get<\/code> method:<\/p>\n<pre class=\"language-python\"><code class=\"language-python\">form <span class=\"token operator\">=<\/span> NameForm<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>which results in the following class:<\/p>\n<pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">class<\/span> <span class=\"token class-name\">GetNameView<\/span><span class=\"token punctuation\">(<\/span>View<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">get<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">,<\/span> request<span class=\"token punctuation\">,<\/span> <span class=\"token operator\">*<\/span>args<span class=\"token punctuation\">,<\/span> <span class=\"token operator\">**<\/span>kwargs<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        form <span class=\"token operator\">=<\/span> NameForm<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n        <span class=\"token keyword\">return<\/span> render<span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">,<\/span> <span class=\"token string\">&quot;name.html&quot;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token punctuation\">{<\/span><span class=\"token string\">&quot;form&quot;<\/span><span class=\"token punctuation\">:<\/span> form<span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span>\n\n    <span class=\"token keyword\">def<\/span> <span class=\"token function\">post<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">,<\/span> request<span class=\"token punctuation\">,<\/span> <span class=\"token operator\">*<\/span>args<span class=\"token punctuation\">,<\/span> <span class=\"token operator\">**<\/span>kwargs<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n        form <span class=\"token operator\">=<\/span> NameForm<span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">.<\/span>POST<span class=\"token punctuation\">)<\/span>\n        <span class=\"token comment\"># check whether it&#x27;s valid:<\/span>\n        <span class=\"token keyword\">if<\/span> form<span class=\"token punctuation\">.<\/span>is_valid<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n            <span class=\"token comment\"># process the data in form.cleaned_data as required<\/span>\n            form<span class=\"token punctuation\">.<\/span>save<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n            <span class=\"token comment\"># redirect to a new URL:<\/span>\n            <span class=\"token keyword\">return<\/span> HttpResponseRedirect<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;\/thanks\/&quot;<\/span><span class=\"token punctuation\">)<\/span>\n        <span class=\"token keyword\">return<\/span> render<span class=\"token punctuation\">(<\/span>request<span class=\"token punctuation\">,<\/span> <span class=\"token string\">&quot;name.html&quot;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token punctuation\">{<\/span><span class=\"token string\">&quot;form&quot;<\/span><span class=\"token punctuation\">:<\/span> form<span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>As you can see there is some minor duplication of code now, which we will address tomorrow!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-51","title":"Transistioning from function-based views to class-based views","summary":"A simple comparision of what the different styles","date_modified":"2024-03-20T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#views"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-50","content_html":"<p>A day away from Django posts today to celebrate myself getting to Day 50! Which means, with this post, I have now written at least 5000 words this year. Which brings me onto days topics and a question:<\/p>\n<p>Which of these two goals seem more achieveable?<\/p>\n<ol>\n<li>Write 100 words every weekday for a year.<\/li>\n<li>Write 25000 words in a year.<\/li>\n<\/ol>\n<p>To me, the second goal is far more daunting than the first, I would have thoughts of &quot;How can I write 25000 words?&quot; and &quot;I&#x27;m not a writer&quot;.\nHowever the trick here is that the first goal would produce more 1000 more words in year than the second!<\/p>\n<p>The key difference is the framing of said goal. The first goal focuses on the input required to achieve a broader goal of writing more, where as the second goal focuses on the output.\nWhen you can I would advise framing your goals in terms of the input required as there are a number of benefits to doing so, chiefly:<\/p>\n<ul>\n<li>The goal hacks your brain to make the goal more achieveable.<\/li>\n<li>It will often be phrased to fit in with your daily or weekly routine which grounds it practically in your life<\/li>\n<li>This then leads to the forming of a routine of a habit which will then in turn change your identity (in this case being a writer)<\/li>\n<li>Progress is consistent and predictable which means your more likely to achieve the goal, unlike many new years resolutions.<\/li>\n<\/ul>\n<p>So next time you want to lose weight or write a novel, think about the framing of that goal.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-50","title":"Input versus output goals","summary":"Framing your goals matters to your success","date_modified":"2024-03-19T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#meta","#goals"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-49","content_html":"<p>Finally there are lessons to be learnt from the first event, while there was nothing horribly wrong, they are missteps to avoid again.<\/p>\n<p>Firstly any virtual sprints ought not occur at the same time as any conference sprints, this mainly caused confusion especially since there was an online component to the US conference sprint.<\/p>\n<p>Next any organiser should probably not try to participate in any particlar project, but be around on text and voice to engage in any newcomers who are joining.<\/p>\n<p>Finally having multiple organisers, ideally in different timezones, would be key to running a continuous event over 24 hours amd to help even out the admin tasks and properly advertise the event to the community!\nBeing a solo organiser just isn&#x27;t much fun, this likely partly on me not reaching out to others though.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-49","title":"Django Virtual Sprints - Part 5","summary":"What should not be repeated!","date_modified":"2024-03-18T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#sprints","#discord"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-48","content_html":"<p>While the event was a great prototype, a few of us came away with some ideas and improvements for next time, below are a few that I am personally interested in trying:<\/p>\n<ul>\n<li>Fewer projects or tables to provide more focus and therefore easier for newer folks to get involved with.<\/li>\n<li>Extend the event to be more asynchronous in nature, for example have a month where we focus on one project in the Django ecosystem<\/li>\n<li>or shorten the event to a day as it&#x27;s more exhausting being online for the whole day.<\/li>\n<li>This event opened me up to hosting more social events in Discord, since then we have launched a Django.Social Discord server to support the in person meetups and for some virtual meetups in the future!<\/li>\n<li>The Discord server has quite a few begineers so having tutorial sessions (django girls, official tutorial, etc) would be more inclusive and targeted to those who are interested.<\/li>\n<li>A meta option would be to also work on ideas related to the Discord itself, we have plenty of things that we are not making use of and could have a hand with, these also include plenty of non-code items!<\/li>\n<li>Finally is there space for a Djangonaut Space collaboration? They have made excellent contributions to the community and more events might help keep the momentum going?<\/li>\n<\/ul>\n<p>Which is your favourite? Drop me an email (<a href=\"mailto:andrew@softwarecrafts.co.uk\">andrew@softwarecrafts.co.uk<\/a>) and let me know.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-48","title":"Django Virtual Sprints - Part 4","summary":"Improvements for next time","date_modified":"2024-03-15T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#sprints","#discord"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-47","content_html":"<p>Today we talk about the event itself. It ran for 2 days (Thursday &amp; Friday) mostly on European timezones. The projects involved were Django Social, Accessibility, Dj-notebook and Django CMS.<\/p>\n<p>We started the sprint in a voice channel with brief intros and an explanation of how things should work out, then we jumped into the respective voice channels for each project and got to work.\nFriday was a repeat of Thursday albet with less people joining in that day. On Friday I ended streaming my work on Django Social for those interested to watch what was going on.<\/p>\n<p>Highlights for me were the Django Social table on Thursday turning into a virtual social, with not a huge amount of work getting done, but community building instead.\nThen on Friday getting a tour by Kojo of the in-person DjangoCon US Sprints in the Catcus Offices!<\/p>\n<p>Tomorrow: Improvements for next time.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-47","title":"Django Virtual Sprints - Part 3","summary":"The sprint itself!","date_modified":"2024-03-14T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#sprints","#discord"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-46","content_html":"<p>This started in this <a href=\"https:\/\/forum.djangoproject.com\/t\/status-of-5-0-alpha-release\/23805\">forum post<\/a> where the question was raised if we still do online sprints since IRC isn&#x27;t used as much as it was.\nYou can see that I effectively raise my hand to suggest Discord as a possible location.<\/p>\n<p>I then gauged interest in <a href=\"https:\/\/forum.djangoproject.com\/t\/online-sprints-whos-interested\/24047\">this post<\/a> and it snowballed from there. Tim suggested a date to coinside with DjangoCon US Sprints,\nI setup a quick <a href=\"https:\/\/docs.google.com\/forms\/d\/e\/1FAIpQLSecu_RiUKZAPtiZ5m-2ZsXzYBaTs76WwuaXboVmOpLw0XG88Q\/viewform?usp=sf_link\">form<\/a> for those to register their interest in participating and a second <a href=\"https:\/\/airtable.com\/apptxrcIVJmY0EQWe\/shro06g0cSZzDl2Br\">form<\/a> for those wanting to submit projects.\nThe first form was then shared on socials and in the Django News newletter, the second was emailed to those who registered an interest in leading a project<\/p>\n<p>Finally we setup a section for Sprints in the Discord server and reminded people a few days before the event of the details and the draft agenda!<\/p>\n<p>Tomorrow: How the event went!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-46","title":"Django Virtual Sprints - Part 2","summary":"How it started and was organised","date_modified":"2024-03-13T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#sprints","#discord"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-45","content_html":"<p>Last year we held the first Django contribution sprint on the Django Discord. As a prototype event I believe it was a great success, people joined, worked on projects and socialised.\nI personally got to chat with people from at least 3 or 4 different continents which was fun and a demonstration of the reach of the community we have.<\/p>\n<p>Over the next series of posts I am going to post about what went into organising this sprint, what went well, what could be have been better and what the next steps could be.<\/p>\n<p>I will then collate these into a longer blog post for a wider audience and easier sharing!<\/p>\n<p>Tomorrow: How it started and got organised.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-45","title":"Django Virtual Sprints","summary":"An overview of the first Discord Sprint from last year","date_modified":"2024-03-12T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#sprints","#discord"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-44","content_html":"<p>One area newer Django engineers struggle with a lot is that of hosting their project once it is ready to go. This comes down to the fact that there are a lot of options available each with it&#x27;s own way of configuring the site to work as intended,\nand Django itself does not favour one method over another.<\/p>\n<p>To that end I wonder if there was a demand for either a repository along with documentation to setup a production-ready Django site. Cookiecutter and the like is a good start, but we would actually want to make really easy to either drop the Django project into the repository, or\ndrop the repository into the Django project... Documentation and automation would be the key building block here!<\/p>\n<p>The other approach would be to sell this as a service, not hosting the Django project itself, but taking a codebase and deploying it to some infrastructure and make it accessible to the right places.\nThe key challenge would be limiting the options available to quickly repeat the steps required.<\/p>\n<p>Do either of these sound viable? Drop me and email if your interested!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-44","title":"Paid Django project configurations","summary":"Another potential reveune opportunity","date_modified":"2024-03-11T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#business","#productized","#service"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-43","content_html":"<p>This week I have been considering a new reveune opportunity for Software Crafts. Essentially the proposition is some form of paid mentoring\/coaching to other software engineers who need help with Django in some way.\nThis could be helping them land a job, build a portfolio and keeping them accountable to that, or perhaps it&#x27;s helping with a tricky techincal problem and you need help breaking it down into managable chunks.\nThe other persona I&#x27;m thinking about is a team lead wanting help with hiring or mentoring junior&#x27;s on their team.<\/p>\n<p>This is one of a few experiments I am going to be running this year to explore what Software Crafts can offer as a business other than purely software development.<\/p>\n<p>If you&#x27;re interested book a slot <a href=\"https:\/\/savvycal.com\/softwarecrafts\/mentoring\">here<\/a><\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-43","title":"Django Mentoring\/Coaching","date_modified":"2024-03-08T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#business","#mentoring"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-42","content_html":"<p>Our final reason for hiring some fractional talent to your team is all about leveraging your existing team to their best potential. Imagine you want your current team to be focused on a large new feature, but there is no capacity\nto tend to the existing applications or even some applicationns are stuck and need to be upgraded from Django 1.X to the latest and greatest.\nThis is where a fractional engineer can fill the gap, allowing the team to push forward safe in the knowledge that no application is being left behind to rot with technical debt.<\/p>\n<p>Or perhaps you want to experiment with some new ideas, but impending deadlines won&#x27;t allow for a new idea, then a fractional engineer would be the perfect solution to build a prototype and demo it back to the team for them to take it forward.<\/p>\n<p>The point here is that a fractional engineer can lighten the burden for a team when the focus requires not to be everywhere at once, a fractional engineer can quietly work in the background to keep the balance of old vs new work getting done and shipped.<\/p>\n<p>And so we come to the end of this mini-series of posts on fractional development. If any of these reasons resonated with your current situation, then drop me an <a href=\"mailto:andrew@softwarecrafts.co.uk\">email<\/a> to see how I can help you.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-42","title":"Fractional Django Development - Part 7","summary":"Why hire a fractional developer? - Lighten the maintenance burden","date_modified":"2024-03-07T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#fractional","#hiring"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-41","content_html":"<p>Are your senior engineers at capacity? Are struggling to achieve best practices in terms of tooling that would speed them up over the short term and long term? Then this would be a perfect fit for a fractional engineer.\nWe can be a sounding board for a tricky problem, someone who can fix the papercuts slowing a team down or be a coach or mentor to junior members when the seniors are tackling the next big feature.<\/p>\n<p>We bring our expertise and experience to accelerate the growth of a team so each member can have a greater impact with the work they do. We can smooth processes, adding tooling, guide decisions or just be a rubber duck!<\/p>\n<p>Final part tomorrow: Lighten the maintenance burden on your team<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-41","title":"Fractional Django Development - Part 6","summary":"Why hire a fractional developer? - Force multipler for a team","date_modified":"2024-03-06T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#fractional","#hiring"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-40","content_html":"<p>Hiring a fractional developer allows them to contribute to the communities they are involved in, for me that&#x27;s the Django Community. But why would a potential client care about me contributing to these communities?<\/p>\n<p>In the case of the Django community, there are a number of benefits for the engineer and client.<\/p>\n<ol>\n<li>A potential pool for new team members when required<\/li>\n<li>Easy access to expert knowledge from the community for those thornier issues in a codebase.<\/li>\n<li>Financial contributions ensure that the community thrives<\/li>\n<li>Opportunties to contribute back which raises the profile and reputation of both the engineer and the client. This could be code, conferences or events, financial or just time itself.<\/li>\n<\/ol>\n<p>These are just the tip of the iceberg, but the last point likely has the greatest affect over the long haul.<\/p>\n<p>Tomorrow: Force multipler for a team<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-40","title":"Fractional Django Development - Part 5","summary":"Why hire a fractional developer? - Communuity Contributions","date_modified":"2024-03-05T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#fractional","#hiring"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-39","content_html":"<p>A safe pair of hands means it&#x27;s a fractional engineer you can trust not to break things often, and when they do (it&#x27;s always when, not if), they come in with a solution quickly and efficiently.\nA safe pair of hands optimises code changes and designs not to break, by making the case for tooling and infrastructure that minimise the possibility of breakages in the first place.\nThis tooling includes linters, CI\/CD, infrastructure as code and testing to name a few.<\/p>\n<p>The impact this has means development of features might be slower to begin with, but will in time ramp up to be efficient and quick.\nThis ought to also give reassurance that this happens while a fractional developer serves multiple clients at a time, they are not going to leave your app in a broken state before moving on to the next client!<\/p>\n<p>Tomorrow: Community contributions<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-39","title":"Fractional Django Development - Part 4","summary":"Why hire a fractional developer? - A safe pair of hands","date_modified":"2024-03-04T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#fractional","#hiring"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-38","content_html":"<p>As someone who works solely with clients and projects where Django is a core part of the stack, each client gets to benefit from each other indirectly from a fractional engineer.\nThis happens most often with the necessary non-functional requirements of a project, such as setting up new infrastructure, improving tooling (linters, formatters etc), CI, tests and the dev environment.<\/p>\n<p>And the knowledge sharing doesn&#x27;t stop there, being a fractional engineer means I get exposed to multiple projects so I have seen my fair share good practices and bad practices which means I often let codebases and database\ndesign from one client influence another. This is obviously not a direct copy &amp; paste scenario, but it enables me to see the effect of different packages and design choices more quickly which means the learnings compound more quickly.<\/p>\n<p>All in all this benefit happens for clients by simple osmosis for knowledge in my brain.<\/p>\n<p>Tomorrow: Safe pair of hands!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-38","title":"Fractional Django Development - Part 3","summary":"Why hire a fractional developer? - Knowledge Sharing","date_modified":"2024-03-01T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#fractional","#hiring"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-37","content_html":"<p>Budget is first on the list of reasons from yesterday&#x27;s post. This reason is probably fairly obvious, but depending on what is required a fractional developer can cost you less that a full-time employee.<\/p>\n<p>For starters we likely run our own company so there is no need to deal with the costs of an employee and since they aren&#x27;t a full-time contractor IR35 in the UK becomes less of an issue (Disclaimer: this isn&#x27;t legal advice!).\nNext you are going to get more for your money, a fractional developer will not be charging by the hour or day, but on deliverables, this means you get what you asked for at the price specified and not an estimated price that could\nchange over time.<\/p>\n<p>This means a fractional developer is particularly suited to those with a tight budget.<\/p>\n<p>Tomorrow: Knowledge sharing<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-37","title":"Fractional Django Development - Part 2","summary":"Why hire a fractional developer? - Budget","date_modified":"2024-02-29T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#fractional","#hiring"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-36","content_html":"<p>I am going to use the next few posts to talk about fractional development, mostly for my own benefit to voice and work through the arguments for and against the business model I am trying right now.\nIt&#x27;s probably not clear, but I am available on a fractional basis where instead of charging by the hour or day, I have a series of fixed price packages for ongoing work or I can produce a quote for fixed scope project.\nI have a number of clients so switching to a full-time contract is not an option, nor one I want to consider anytime soon, I like the flexibility!<\/p>\n<p>We will explore the benefits in upcoming days, but off the top of my head I will start with the following topics:<\/p>\n<ol>\n<li>Budget<\/li>\n<li>Knowledge sharing<\/li>\n<li>Safe pair of hands<\/li>\n<li>Community contributions<\/li>\n<li>Force multipler for a team<\/li>\n<li>Lighten the maintenance burden on your team<\/li>\n<\/ol>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-36","title":"Fractional Django Development - Part 1","summary":"Why hire a fractional developer?","date_modified":"2024-02-28T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#fractional","#hiring"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-35","content_html":"<p>So you have started learning Django and you have completed the first project that you want to put out into the world. So the next question is hosting this website?\nWe often get the question in the Discord server of how can I host it for free?<\/p>\n<p>While this is possible, there are plenty of platforms that offer free plans, these come with plenty of limitations either of time, usage or technical limitations, I would really advise asking yourself this question.<\/p>\n<blockquote>\n<p>What I am optimising for...?<\/p>\n<\/blockquote>\n<p>This question can be applied to pretty much every situation in life, but here lets end it with the following options:<\/p>\n<ol>\n<li>What I am optimising for by learning Django?<\/li>\n<li>What I am optimising for by hosting this Django project?<\/li>\n<\/ol>\n<p>The answer to these questions can be varied but, and getting to my point of today&#x27;s post, if the answer is along the lines of getting a job, selling a product or seriously advancing your career then I would advise spending some dollars on hosting.<\/p>\n<p>The cost of a domain name and a server ought not put you off producing a professional finished project. You have already invested significant time getting to this point, you need to invest some money as well.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-35","title":"The cost of a simple Django website","summary":"How much money should you pay to host a Django website?","date_modified":"2024-02-27T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#hosting"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-34","content_html":"<p>Another very common migration pattern I have come across over the years, is what to do when adding a required (non-nullable) field to an existing model. Let&#x27;s set the scene, say you have a model called Post that looks as roughly follows:<\/p>\n<pre class=\"language-python\"><code class=\"language-python\">\n<span class=\"token keyword\">class<\/span> <span class=\"token class-name\">Post<\/span><span class=\"token punctuation\">(<\/span>models<span class=\"token punctuation\">.<\/span>Model<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n  title <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>Charfield<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">)<\/span>\n  content <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>TextField<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">)<\/span>\n  author <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>ForeignKey<span class=\"token punctuation\">(<\/span>User<span class=\"token punctuation\">,<\/span> <span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">)<\/span>\n  slug <span class=\"token operator\">=<\/span> modelds<span class=\"token punctuation\">.<\/span>SlugField<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>and you have been tasked with updating the URL to replace the the use of the primary key id with an external identifier. One might think the adding the following field defintion would be sufficient:<\/p>\n<pre class=\"language-python\"><code class=\"language-python\">  uuid <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>UUIDField<span class=\"token punctuation\">(<\/span>default<span class=\"token operator\">=<\/span>uuid<span class=\"token punctuation\">.<\/span>uuid4<span class=\"token punctuation\">,<\/span> null<span class=\"token operator\">=<\/span><span class=\"token boolean\">False<\/span><span class=\"token punctuation\">,<\/span> editable<span class=\"token operator\">=<\/span><span class=\"token boolean\">False<\/span><span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>Unfortunately this will throw an error during the migration since, the callable in the default will return the same uuid for each row. Instead you will need to follow these three steps:<\/p>\n<ol>\n<li>Add the field with <code>null=True<\/code> so:<\/li>\n<\/ol>\n<pre class=\"language-python\"><code class=\"language-python\"> uuid <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>UUIDField<span class=\"token punctuation\">(<\/span>default<span class=\"token operator\">=<\/span>uuid<span class=\"token punctuation\">.<\/span>uuid4<span class=\"token punctuation\">,<\/span> null<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">,<\/span> editable<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<ol start=\"2\">\n<li>Add a data migration to manually assign a uuid4 value to each existing instance of the model (simple loop and save will be sufficient here)<\/li>\n<li>Update the field to make <code>null=False<\/code> and <code>editable=False<\/code> will will leave your field in the desired result.<\/li>\n<\/ol>\n<p>And your done! and freely available to use this field in your URL without worrying about the value being null.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-34","title":"Django migrations, backfilling data","summary":"Adding a new field that needs data","date_modified":"2024-02-26T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#migrations"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-33","content_html":"<p>While the Django migration system can automatically detect a lot of use cases, there are times when we as developers need to give a bit of guidance to the system to ensure we don&#x27;t loose any data.\nThe pattern below often occurs when transforming data types in some manner.<\/p>\n<p>The high level steps are as follows:<\/p>\n<ol>\n<li>Introduce new field or model, if required.\na. If it&#x27;s a model add new matching relations alongside the existing relations<\/li>\n<li>Perform a data migration that transforms the data to the new field\/model\na. Again with a model, remember to migrate all the of the relations as well.<\/li>\n<li>Update all other references in the code base to use the new field\/model. Alternatively this can be updating both fields for a time to have a slower migration if required.<\/li>\n<li>Push all of this through to production and check it works ok.<\/li>\n<li>Then remove the old field\/model.<\/li>\n<li>Deploy again.<\/li>\n<li>Optionally rename the new field\/model if required. This is typicial if you have a field called <code>foo<\/code> and the new field is called <code>new_foo<\/code> which at this point is no longer new so should revert to just <code>foo<\/code> again.<\/li>\n<\/ol>\n<p>This should help get you out of quite a few more complex situations, and deploying after each step here, will reduce the uncertainly of potential data loss and confusion.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-33","title":"Django migrations, a typical pattern","summary":"A common pattern for moving data around.","date_modified":"2024-02-23T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#migrations"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-32","content_html":"<p>Today is quick retrospective post for roughly a month into this habit I am building. Doing it only on weekday&#x27;s has been very sensible as I really don&#x27;t have the capacity to sit and write at weekends,\na young family and other commitments keep weekends busy but also relaxed in that I don&#x27;t need to open my laptop to do some &#x27;work&#x27;.<\/p>\n<p>The concern I have is keeping the content flowing. To date I have been churning through a backlog of content ideas that I have noted down in the past, but that list is almost finished. This means I will need to do\nadapt my process for gaining writing topics, this either means writing about my work or spending time exploring topics to write about. Both will be challenging, but doable.<\/p>\n<p>Here&#x27;s to the next month!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-32","title":"100 words a month in","summary":"How is it going for me?","date_modified":"2024-02-22T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#meta","#retro"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-31","content_html":"<p>Following on from yesterday&#x27;s post about determining the source of truth I thought I would follow up with two examples of potential solutions.<\/p>\n<p>First up we have the case where we two sources, an API and the database. In this case we want know deterministically which data should take priority,\ntherefore we can process the data from the API in the background and and save the relevant updated fields to the database. This keeps the overall system flow of data simpler.<\/p>\n<p>Here would be an example of an update function:<\/p>\n<pre class=\"language-python\"><code class=\"language-python\">\n<span class=\"token keyword\">def<\/span> <span class=\"token function\">update_from_api<\/span><span class=\"token punctuation\">(<\/span>instance<span class=\"token punctuation\">,<\/span> data_from_api<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n  <span class=\"token comment\"># Example where API takes priority<\/span>\n  <span class=\"token comment\"># This assumes data types match up completely.<\/span>\n  <span class=\"token comment\"># This could fail if the updated field from the API contains an empty string<\/span>\n  instance<span class=\"token punctuation\">.<\/span>updated <span class=\"token operator\">=<\/span> data_from_api<span class=\"token punctuation\">.<\/span>get<span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;updated&#x27;<\/span><span class=\"token punctuation\">,<\/span> instance<span class=\"token punctuation\">.<\/span>updated<span class=\"token punctuation\">)<\/span>\n\n  <span class=\"token comment\"># Example where the DB takes priority if the DB has data.<\/span>\n  <span class=\"token keyword\">if<\/span> <span class=\"token keyword\">not<\/span> instance<span class=\"token punctuation\">.<\/span>created<span class=\"token punctuation\">:<\/span>\n    instance<span class=\"token punctuation\">.<\/span>created <span class=\"token operator\">=<\/span> data_from_api<span class=\"token punctuation\">.<\/span>get<span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;created&#x27;<\/span><span class=\"token punctuation\">,<\/span> instance<span class=\"token punctuation\">.<\/span>created<span class=\"token punctuation\">)<\/span>\n\n  instance<span class=\"token punctuation\">.<\/span>save<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>Tomorrow we will look at a more complex example where the user would need to be involved in the solution.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-31","title":"Source of Truth - Part 2","summary":"Some example solutions for dealing with source of truth in Django","date_modified":"2024-02-20T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#system_design","#truth","#django"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-30","content_html":"<p>This topic is probably going to cover a few days this week. I refer to it as the &quot;source of truth&quot; for data in a system. I have often worked on systems where data can arrive in to the system from a variety of sources, typically its from third-party APIs and various user inputs.<\/p>\n<p>A question then typically arises of what to do when data from these various sources conflicts with each other. This is especially the case if there is some automated updates that means we need to cater for understanding which source of data should take priority.\nIs it the API or the current state of the database? Generally this requires a conscious decision to be made in the business or organisation for work to move forward, otherwise as a developer you are going to be stuck with bug reports or similar until it is resolved.<\/p>\n<p>Essentially get clear on where your source of truth is for each item of data in your system, especially when you can get it from multiple locations. Tomorrow I will talk through an example of addressing this.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-30","title":"What is your source of truth","summary":"Be clear in your system design where is the truth of data is.","date_modified":"2024-02-19T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#system_design","#truth"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-29","content_html":"<p>Today I wasn&#x27;t sure what to write about; none of the technical topics on my list jumped out at me today. But today was a very Django heavy day, I hosted the first Django Social in Cmabridge of the year.\nSeven people turned up to some coworking in BrewDog Cambridge and while 7 may not sound like much it was 100% attendance and one of our larger gatherings since we began last year.<\/p>\n<p>In general I would definitely recommend a coworking session, but don&#x27;t expect to be as productive as some lively discussion was had as well as food and drink.<\/p>\n<p>Finally good luck to all those who applied for the Django Fellow position, as I write this I think it is now closed to new applications, so now the waiting begins to see who gets the role!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-29","title":"Django Social Cambridge","summary":"The first social of 2024","date_modified":"2024-02-16T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#social"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-28","content_html":"<p>The idea of a <code>prodserver<\/code> command has been buzzing in my head so I am putting pen to paper to work out what it might look like. As mentioned yesterday <a href=\"https:\/\/github.com\/lincolnloop\/django-webserver\">django-webserver<\/a> has an excellent start in this space by providing four management commands, each for a separate wsgi server.\nHowever what I don&#x27;t like about this is that it lacks a clear common interface when running the actual command.<\/p>\n<p>While reviewing the django-webserver code, another consideration the pops out is how to handle the installation of the wsgi\/asgi servers themselves and then the interfacing to those servers within the python code.\nThe crux here is to provide a clear API that allows for easy extension to other servers without much friction otherwise, it&#x27;s not much of an improvement.<\/p>\n<p>Finally I do want the command to be able to specify multiple servers. For example running a WSGI and ASGI server in the same project or perhaps other background workers. Here a similar interface to running <code>docker compose<\/code> comes to mind, for example:<\/p>\n<pre class=\"language-shell\"><code class=\"language-shell\">.\/manage.py prodserver web-sync\n.\/manage.py prodserver web-async\n.\/manage.py prodserver worker\n<\/code><\/pre>\n<p>Where the names are user specified in settings, which might look like this:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token comment\"># could be a list instead of a dictionary<\/span>\nPROD_SERVERS <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">{<\/span>\n  <span class=\"token string\">&#x27;web-sync&#x27;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token punctuation\">{<\/span>\n    <span class=\"token string\">&quot;BACKEND&quot;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">&#x27;prodserver.wsgi.gunicorn.GunicornServer&#x27;<\/span>\n    <span class=\"token string\">&#x27;OPTIONS&#x27;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token punctuation\">{<\/span>\n      <span class=\"token string\">&#x27;ARGS&#x27;<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token punctuation\">[<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">,<\/span>\n\n    <span class=\"token punctuation\">}<\/span>\n\n  <span class=\"token punctuation\">}<\/span>\n<span class=\"token punctuation\">}<\/span>\n<\/code><\/pre>\n<p>where the <code>GunicornServer<\/code> class could look like:<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token comment\"># borrowed from django-webserver<\/span>\n<span class=\"token keyword\">class<\/span> <span class=\"token class-name\">GunicornServer<\/span><span class=\"token punctuation\">(<\/span>BaseProdServer<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n\n  <span class=\"token keyword\">def<\/span> <span class=\"token function\">start_server<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">,<\/span> <span class=\"token operator\">*<\/span>args<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    DjangoApplication<span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;%(prog)s [OPTIONS]&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">.<\/span>run<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n\n<\/code><\/pre>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-28","title":"What would a prodserver do?","summary":"Continuing from yesterday's idea","date_modified":"2024-02-15T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#internals","#prodserver"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-27","content_html":"<p>Yesterday, while helping someone on Discord I had a flash of an idea that will hopefully make it clearer when to use <code>runserver<\/code> and when not to.\nWhile there is a warning when you spin up <code>runserver<\/code> about only being suitable for development, this is likely noted the first time around and forgotten about when it comes to deploying to production.<\/p>\n<p>Hence my proposal to the community <a href=\"https:\/\/forum.djangoproject.com\/t\/proposal-rename-runserver-command-to-devserver-and-add-a-prodserver-management-command\/28030\">here<\/a><\/p>\n<p>It consists of two parts:<\/p>\n<ol>\n<li>Rename the <code>runserver<\/code> command to <code>devserver<\/code> or a similar name the key thing here is to have <code>dev<\/code> in there somewhere<\/li>\n<li>Introduce a new command <code>prodserver<\/code> which is designed to provide a common interface to a wsgi\/asgi server which is configured from settings.<\/li>\n<\/ol>\n<p>Since proposing this in the forum, one response led me to discover <a href=\"https:\/\/github.com\/lincolnloop\/django-webserver\">django-webserver<\/a> which has similar aims and I will either contributing back to it or using it as inspiration for my own package.<\/p>\n<p>Future days will revisit this proposal. It&#x27;s a simple wording change, but one with a large &amp; wide impact!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-27","title":"My first proposal to Django","summary":"Will a simple wording change make into Django?","date_modified":"2024-02-14T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#internals","#naming"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-26","content_html":"<p>Today is a sort of cheatsheet for properly formatting a Django codebase. There are 2 main issues that I see newer devs getting wrong, the formatting of names and when to use singular vs plural (there may be others, but that will be enough for today!)<\/p>\n<p>Any Python class should be PascalCase, functions and methods should snake_case, variables should be snake_case. An example:<\/p>\n<pre class=\"language-python\"><code class=\"language-python\">\n<span class=\"token keyword\">class<\/span> <span class=\"token class-name\">BlogPage<\/span><span class=\"token punctuation\">(<\/span>models<span class=\"token punctuation\">.<\/span>Model<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n  a_long_title <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>CharField<span class=\"token punctuation\">(<\/span>max_length<span class=\"token operator\">=<\/span><span class=\"token number\">255<\/span><span class=\"token punctuation\">)<\/span>\n\n  <span class=\"token keyword\">def<\/span> <span class=\"token function\">a_method_to_render_the_blog<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    <span class=\"token comment\"># do something<\/span>\n\n<span class=\"token keyword\">def<\/span> <span class=\"token function\">a_function_to_do_something<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n  <span class=\"token comment\"># do something<\/span>\n<\/code><\/pre>\n<p>The second issue of plural vs singular is that most class definitions should be singular, especially model classes as this represents a table in SQL and instances represent many rows, so should be plural.<\/p>\n<p>\u274c<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token keyword\">class<\/span> <span class=\"token class-name\">BlogPages<\/span><span class=\"token punctuation\">(<\/span>models<span class=\"token punctuation\">.<\/span>Model<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n  <span class=\"token comment\"># ...<\/span>\n<\/code><\/pre>\n<p>\u2705<\/p>\n<pre class=\"language-py\"><code class=\"language-py\"><span class=\"token keyword\">class<\/span> <span class=\"token class-name\">BlogPage<\/span><span class=\"token punctuation\">(<\/span>models<span class=\"token punctuation\">.<\/span>Model<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n  <span class=\"token comment\"># ...<\/span>\n<\/code><\/pre>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-26","title":"Django Naming Conventions","summary":"How to make your codebase make sense in English","date_modified":"2024-02-13T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#tips","#naming"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-25","content_html":"<p>The most usual and typical place DRF Serializers turn up is a as a parallel to Django Forms for processing and validating incoming data from a client.\nHowever DRF Serializers are far more flexible than most developers realise. They are simply a declarative way to declare the shape of some valid data (before type hints existed.)<\/p>\n<p>Therefore it would be entirely logical to use Serializers to validate data when processing data through an API response to then either save to your database or pass to your clients.\nAnother situation could be serializing data to dump into object storage like S3 or using them to import data via a management command.<\/p>\n<p>Simply put Serializers do not need to be tied to a view to be effective in the management and validation of data.<\/p>\n<p>All that said, pydantic might be a better fit for these use cases in a fresh project, but DRF is a solid option to reuse an existing dependency and have all the nice hooks into Django.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-25","title":"Situations to use DRF Serializers","summary":"They are useful not just for REST API views","date_modified":"2024-02-12T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#drf","#rest","#api"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-24","content_html":"<p>Django Rest Framework (DRF) has been a long standing default option when it comes to requiring a web API for a Django project. At the core of DRF are Fields (not Serializers!).\nFields are essentially a mechanism for performing validation, serialization and deserialization of data.<\/p>\n<p>Serializers are a subclass of a Field which simply cater for a grouping of fields. DRF then provides the expected hooks to easily integrate with models via ModelSerializers.<\/p>\n<p>Typically the provided views and viewsets (multiple grouped views) can then expose these Serializers out to the web, but they are not limited to just being used in views.<\/p>\n<p>Finally DRF provides all the necessary features required for an API such as permissioning, authentication, rate limiting, documenation, django-filter integration and more!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-24","title":"Django Rest Framework in 100 words","summary":"Need an API? DRF is a solid option to get started with","date_modified":"2024-02-09T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#drf","#rest","#api"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-23","content_html":"<p>The Django Admin is a powerful out of the box tool that you get for free when starting any project. Yes, by today&#x27;s standard&#x27;s it looks old and yes every developer under the sun has pushed it to do things it clear shouldn&#x27;t do and some even try to have it as the main UI for their project (don&#x27;t do this please)!\nThis to me shows just how powerful the tool is! It is incredible. However I wonder how many Django projects out in the wild have multiple Admin Sites?<\/p>\n<p>Typically everyone uses the default instantiation of the <code>AdminSite<\/code> class, but there is no reason why you cannot either instantiate it multiple times or subclass it to add your own customisation.\n(In fact one of my clients does the second option to provider a very customised dashboard for staff.)<\/p>\n<p>The idea around having 2 admins could be that you limit what is available to staff compared with developers. Or easily limit the availablilty of personal data for compliance reasons (ie GDPR or HIPAA). On the surface it is probably a lot of duplicate code, but it an idea I would like to explore one day.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-23","title":"An under used feature of the admin? - Multiple AdminSites","summary":"Did you know that it's possible to have multiple admins","date_modified":"2024-02-08T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#admin","#django"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-22","content_html":"<p>If I am lucky enough to start a greenfield project these days and I knew this was going into production at some point, my defaults are as follows. I use django-cookiecutter as a template selecting Docker, Celery and Sentry, the other settings I care less about immediately. Our database is always Postgres.<\/p>\n<p>I then add the following to the local setup. First up is to create a <code>ca<\/code> container service which uses <code>mkcert<\/code> to allow for local self-signed SSL certs for the whole dev environment. This includes SSL between Django and Postgres and any connection to the browser. It also means I have some nice hostnames by editing <code>\/etc\/hosts<\/code><\/p>\n<p>I also default to adding <code>minio<\/code> as a container service to allow for an S3 API interface locally for static and media files. This should mean that I can work offline if required.<\/p>\n<p>Finally I tend to add a <code>.shortcuts<\/code> file with handy aliases specific to the project which can be activiated using <code>source .shortcuts<\/code><\/p>\n<p>But I am rarely this lucky so I am normally iterating towards this type of setup depending on the state of the codebase!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-22","title":"My default Django development setup","summary":"... or how I start a new project in recent years","date_modified":"2024-02-07T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#dev","#environment","#tooling","#django"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-21","content_html":"<p>This is day 21 and for various reasons I did not get around to writing these 100 words this morning and now it&#x27;s evening! I feel more stressed about it, which is definitely a sign that this a morning routine and I must not get sucked\ninto work before I have finished this!<\/p>\n<p>I am starting to also feel the mild anxiety of working out what to write each day, I do have topics in my list, but I am finding the thought of breaking them down tough as well as the lack of context I made to myself a while back frustrating!<\/p>\n<p>If you are reading this and have an idea for a post or set of posts then do get in touch!<\/p>\n<p>See you tomorrow!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-21","title":"20 days done","summary":"I almost missed today!","date_modified":"2024-02-06T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#meta"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-20","content_html":"<p>The title might seem extreme, but there is a truth behind the statement. First a little back story, on the Django Discord we have begineers who have messed up their migrations somehow.\nThis typically comes down to the fact that they have deleted the files but not dealt with the state of the database and don\u2019t understand how the 2 are related. Since this is typically their first project, a simple solution is to delete the whole database and start from scratch.<\/p>\n<p>Now I would hope it would be obvious why the above solution isn&#x27;t ideal, especially in a production context. Purposely nuking the whole production database of a company is a quick way to being out of a job!\nIn the above stories, I have yet to understand the specific reasoning for begineers deleting migration files, but if they were to take the hueristic to treat their development environment as a production environment, I would hope caution would be more front of mind.<\/p>\n<p>Generally there is always a way forward to fix a problem that doesn&#x27;t completely require a destructive solution. Treatding development as a production environment gives you this practice to think and consider alternative solutions.<\/p>\n<p>For the record, my current development pattern means I rarely delete and recreate my development database and if I did, it would be due to certain features or requirements, not down to something being broken!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-20","title":"Your development environment is production","summary":"A thought experiment for when things in development go wrong","date_modified":"2024-02-05T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#heuristic","#tip","#rule_of_thumb"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-19","content_html":"<p>There will come a time in any Django developer&#x27;s career when you will actually need to look at the SQL that is going on under the hood. For myself that is typically to understand the difference between 2 queries when doing a refactor.<\/p>\n<p>There are a few ways to do this, and this depends on the context where the query exists, but for me the easiest way is to print the <code>query<\/code> attribute of any QuerySet as shown below.<\/p>\n<pre class=\"language-python\"><code class=\"language-python\">\n<span class=\"token operator\">&gt;&gt;<\/span><span class=\"token operator\">&gt;<\/span> low_stock <span class=\"token operator\">=<\/span> Product<span class=\"token punctuation\">.<\/span>objects<span class=\"token punctuation\">.<\/span><span class=\"token builtin\">filter<\/span><span class=\"token punctuation\">(<\/span>quantity__lt<span class=\"token operator\">=<\/span><span class=\"token number\">5<\/span><span class=\"token punctuation\">)<\/span>\n<span class=\"token operator\">&gt;&gt;<\/span><span class=\"token operator\">&gt;<\/span> <span class=\"token keyword\">print<\/span><span class=\"token punctuation\">(<\/span>low_stock<span class=\"token punctuation\">.<\/span>query<span class=\"token punctuation\">)<\/span>\nSELECT <span class=\"token string\">&quot;temp_product&quot;<\/span><span class=\"token punctuation\">.<\/span><span class=\"token string\">&quot;id&quot;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token string\">&quot;temp_product&quot;<\/span><span class=\"token punctuation\">.<\/span><span class=\"token string\">&quot;name&quot;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token string\">&quot;temp_product&quot;<\/span><span class=\"token punctuation\">.<\/span><span class=\"token string\">&quot;description&quot;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token string\">&quot;temp_product&quot;<\/span><span class=\"token punctuation\">.<\/span><span class=\"token string\">&quot;quantity&quot;<\/span> FROM <span class=\"token string\">&quot;temp_product&quot;<\/span> WHERE <span class=\"token string\">&quot;temp_product&quot;<\/span><span class=\"token punctuation\">.<\/span><span class=\"token string\">&quot;quantity&quot;<\/span> <span class=\"token operator\">&lt;<\/span> <span class=\"token number\">5<\/span>\n<\/code><\/pre>\n<p>An aside is that we are using <code>manage.py shell<\/code> here to access Django in a python interpreter and really should be the preferred way of doing adhoc queries and work against your project. It WILL level up your Django knowledge and it will be available in every environment that your project exists!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-19","title":"Using the ORM - print the actual SQL","summary":"Ever wondered what SQL is actually used under the hood?","date_modified":"2024-02-02T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#ORM","#sql","#tip"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-18","content_html":"<p>Can you spot the issue with the code below? It&#x27;s an example of a common gotcha that newer Django devs encounter fairly often.<\/p>\n<pre class=\"language-python\"><code class=\"language-python\">urlpatterns <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">[<\/span>\n  path<span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;&lt;slug:slug&gt;&#x27;<\/span><span class=\"token punctuation\">,<\/span> ProductDetailView<span class=\"token punctuation\">.<\/span>as_view<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span> name<span class=\"token operator\">=<\/span><span class=\"token string\">&#x27;detail&#x27;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n  path<span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;add&#x27;<\/span><span class=\"token punctuation\">,<\/span> ProductAddView<span class=\"token punctuation\">.<\/span>as_view<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span> name<span class=\"token operator\">=<\/span><span class=\"token string\">&#x27;add&#x27;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n<span class=\"token punctuation\">]<\/span>\n<\/code><\/pre>\n<p>Seen it yet? In this scenarios the second urlpattern will <em>never<\/em> get used. This is becaused <code>add<\/code> is a valid slug so Django will always match the first urlpattern and then use the first view, which will likely lead to a 404 error\nunless you have a Product with a slug called <code>add<\/code><\/p>\n<p>Thankfully the fix is simple and quick to implement, the lines just need to be switched around.<\/p>\n<pre class=\"language-python\"><code class=\"language-python\">urlpatterns <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">[<\/span>\n  path<span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;add&#x27;<\/span><span class=\"token punctuation\">,<\/span> ProductAddView<span class=\"token punctuation\">.<\/span>as_view<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span> name<span class=\"token operator\">=<\/span><span class=\"token string\">&#x27;add&#x27;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n  path<span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;&lt;slug:slug&gt;&#x27;<\/span><span class=\"token punctuation\">,<\/span> ProductDetailView<span class=\"token punctuation\">.<\/span>as_view<span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span> name<span class=\"token operator\">=<\/span><span class=\"token string\">&#x27;detail&#x27;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n<span class=\"token punctuation\">]<\/span>\n<\/code><\/pre>\n<p>Viola! You can now access the detail page and the add page.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-18","title":"Order matters","summary":"make sure your urlpatterns are correctly ordered!","date_modified":"2024-02-01T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#urls","#tip"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-17","content_html":"<p>Today we end this mini-series on User Types with using Django&#x27;s Groups &amp; Permissions models. By default Django creates four permissions for each model that is created (view, create, edit, delete). These permissions can either be assigned directly to a User or to a Group.\nThere easily hook into limiting access to views with decorators &amp; mixins as well has having the <code>has_perm<\/code> method on the User model to check permissions at other points in the code.\nBy default Permissions only work at a table\/model level, however third-party packages like django-guardian have extended this functionality to allow for row based permissions.\nSince Groups and Permissions are just plain models they can easily be created programmatically and other models can have relations to them.<\/p>\n<p>All of these features mean it is possible to build a fairly complex permissioning system for access to various parts of your system. With object level permissioning you can create a system close to Google Drive&#x27;s or Notion&#x27;s concept of sharing documents<\/p>\n<p>Showing a code example here doesn&#x27;t make much sense since all these features available in the admin to begin playing with. The best place to learn more is to delve into the auth section of the <a href=\"https:\/\/docs.djangoproject.com\/en\/5.0\/topics\/auth\/default\/#permissions-and-authorization\">docs<\/a><\/p>\n<p>The only major disadvantage of this system is that Groups by default are not swappable (which could be seen as a good thing) so storing extra data that needs to be associated with a user type would require the solution from part 2 as well.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-17","title":"Users, Profiles & other things - Part 4","summary":"Finally finishing with Permissions","date_modified":"2024-01-31T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#users","#design"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-16","content_html":"<p>The next approach to different user types is leverage OneToOneField&#x27;s or ForeignKey&#x27;s to various models. This is the most common approach I have encountered in my career and feels the most natural for myself.\nA OneToOneField would be used most often which would limit the user having a single role of a specific type. However ForeignKey&#x27;s would be appropriate if a single User could be, for example, a Manager of multiple entities.<\/p>\n<p>Following yesterday&#x27;s example code (employee, manager, admin). This approach would be as follows:<\/p>\n<pre class=\"language-python\"><code class=\"language-python\">\n<span class=\"token keyword\">class<\/span> <span class=\"token class-name\">User<\/span><span class=\"token punctuation\">(<\/span>AbstractUser<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n  <span class=\"token comment\"># ...<\/span>\n\n<span class=\"token keyword\">class<\/span> <span class=\"token class-name\">Manager<\/span><span class=\"token punctuation\">(<\/span>models<span class=\"token punctuation\">.<\/span>Model<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n  user <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>ForeignKey<span class=\"token punctuation\">(<\/span>User<span class=\"token punctuation\">,<\/span> on_delete<span class=\"token operator\">=<\/span>models<span class=\"token punctuation\">.<\/span>CASCADE<span class=\"token punctuation\">,<\/span> null<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">,<\/span> related_name<span class=\"token operator\">=<\/span><span class=\"token string\">&#x27;manager_positions&#x27;<\/span><span class=\"token punctuation\">)<\/span>\n\n<span class=\"token keyword\">class<\/span> <span class=\"token class-name\">Administrator<\/span><span class=\"token punctuation\">(<\/span>models<span class=\"token punctuation\">.<\/span>Model<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n  user <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>OneToOneField<span class=\"token punctuation\">(<\/span>Administrator<span class=\"token punctuation\">,<\/span> on_delete<span class=\"token operator\">=<\/span>models<span class=\"token punctuation\">.<\/span>CASCADE<span class=\"token punctuation\">,<\/span> null<span class=\"token operator\">=<\/span><span class=\"token boolean\">True<\/span><span class=\"token punctuation\">,<\/span> related_name<span class=\"token operator\">=<\/span><span class=\"token string\">&#x27;admin_role&#x27;<\/span><span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>Pros of this approach is that is separates concerns nicely into seprate models, so the User models remains purely for authentication and doesn&#x27;t get cluttered with extra fields, and again is fairly simple to implement and expand upon when required. This approach also groups related data on each profile model.\nSome negatives is that it will involve more joins when querying what role a user has, which could be problematic if there are a lot of different types.<\/p>\n<p>One variation of this idea which I would like to try is to have a local profile models per app. While this may feel extreme, it would provide locality and tight boundaries on what each django app can access.<\/p>\n<p>Finally tomorrow we will consider using the built in Groups &amp; Permissions models that Django offers.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-16","title":"Users, Profiles & other things - Part 3","summary":"Using relations to manage user types","date_modified":"2024-01-30T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#users","#design"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-15","content_html":"<p>Carrying on from our last post we are going to examine the first option for defining different User types or profiles, which is to add fields to a custom User model. This is the simplest and actually used by Django to define staff and superuser roles.<\/p>\n<p>Typically these fields would either be BooleanField as flags or a CharField with the choices option set. Below is a quick example of how it could work.<\/p>\n<pre class=\"language-python\"><code class=\"language-python\">\n<span class=\"token keyword\">class<\/span> <span class=\"token class-name\">User<\/span><span class=\"token punctuation\">(<\/span>AbstractUser<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n  <span class=\"token comment\"># ...<\/span>\n  is_manager <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>BooleanField<span class=\"token punctuation\">(<\/span>default<span class=\"token operator\">=<\/span><span class=\"token boolean\">False<\/span><span class=\"token punctuation\">)<\/span>\n  is_admin <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>BooleanField<span class=\"token punctuation\">(<\/span>default<span class=\"token operator\">=<\/span><span class=\"token boolean\">False<\/span><span class=\"token punctuation\">)<\/span>\n  role <span class=\"token operator\">=<\/span> models<span class=\"token punctuation\">.<\/span>CharField<span class=\"token punctuation\">(<\/span>max_length<span class=\"token operator\">=<\/span><span class=\"token number\">255<\/span><span class=\"token punctuation\">,<\/span> choices<span class=\"token operator\">=<\/span><span class=\"token punctuation\">(<\/span>\n    <span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;employee&#x27;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token string\">&#x27;Employee&#x27;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;manager&#x27;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token string\">&#x27;Manager&#x27;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n    <span class=\"token punctuation\">(<\/span><span class=\"token string\">&#x27;admin&#x27;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token string\">&#x27;Administrator&#x27;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">,<\/span>\n  <span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>Pros of this approach is that&#x27;s quick and simple to implement, easy to understand and that queries don&#x27;t involve any joins to other tables.\nSome negatives include there is no explicit hierarchy to the roles, any additional roles require code changes and finally it mixes concerns, the User model ought to be purely for authentication, not authorization or permissions.<\/p>\n<p>This approach is certainly one to consider when you need something simple. Tomorrow we will consider using relations.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-15","title":"Users, Profiles & other things - Part 2","summary":"Starting simple with extra fields","date_modified":"2024-01-29T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#users","#design"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-14","content_html":"<p>A common question and\/or issue I see new developers get into with Django is a lack of understanding around the User model and what to do about multiple user types (eg Employee &amp; Manager). Below is a brief summary of the available options to deal with.<\/p>\n<p>First off Django <strong>only allows one<\/strong> User model for authentication. This is the only model where a password or similar should be defined and it should inherit from one of the Abstract User classes.<\/p>\n<p>Next to differentiate between multiple user types or profile there are three common options I can think of:<\/p>\n<ol>\n<li>Have fields on a Custom User model, either a BooleanField as a flag or a field with choices.<\/li>\n<li>Have related models via a ForeignKey or OneToOneField to your user model<\/li>\n<li>Use Django&#x27;s Groups &amp; Permissions Models to differentiate between Users.<\/li>\n<\/ol>\n<p>The above options aren&#x27;t mutually exclusive but your code would probably be confusing if you did all three. Next week will be dive into each of these options and when to use each one.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-14","title":"Users, Profiles & other things","summary":"What do to when you have multiple","date_modified":"2024-01-25T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#users","#design"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-13","content_html":"<p>Django uses python classes for pretty much all major components that you will interact with (Models, Forms, Views, Querysets, etc.) and a lot of these have predefined methods for doing certain things, eg saving a model or\nprocessing a POST request. While this may seem like a lot of magic (and in some places there is some impressive stuff going on), it does make it very easy to jump in to work out what is going on.<\/p>\n<p>I cannot count the times I have done this (especially on Generic Views):<\/p>\n<pre class=\"language-py\"><code class=\"language-py\">\n<span class=\"token keyword\">class<\/span> <span class=\"token class-name\">MyView<\/span><span class=\"token punctuation\">(<\/span>FormView<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n  <span class=\"token comment\"># ...<\/span>\n  <span class=\"token keyword\">def<\/span> <span class=\"token function\">form_invalid<\/span><span class=\"token punctuation\">(<\/span>self<span class=\"token punctuation\">,<\/span> form<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">:<\/span>\n    <span class=\"token keyword\">print<\/span><span class=\"token punctuation\">(<\/span>form<span class=\"token punctuation\">.<\/span>errors<span class=\"token punctuation\">)<\/span>\n    <span class=\"token keyword\">return<\/span> <span class=\"token builtin\">super<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">.<\/span>form_invalid<span class=\"token punctuation\">(<\/span>form<span class=\"token punctuation\">)<\/span>\n<\/code><\/pre>\n<p>This quick override allows me to see if the processed form has any errors and as a result not taking the expected action, or overriding a model save method instead of signals which are hard to follow.<\/p>\n<p>The key thing to remember with Django is that it&#x27;s just Python, so any Python debugging trick you know or read about ought to work in Django (within reason of course!)<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-13","title":"Digging into Django class","summary":"It's snakes all the way down","date_modified":"2024-01-24T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#classes","#super"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-12","content_html":"<p>One thing I often see Django developers do when creating Django forms or Django Rest Framework Serializers is to use the <code>__all__<\/code> shortcut to specify all fields from the Model.\nPersonally I would remove this as an option as it encourages 2 bad practices IMO.<\/p>\n<p>Firstly this introduces a security risk of leaking information later in the codebase&#x27;s life. When at a later date a new model field is added, but doesn&#x27;t want to be exposed to this form then <code>__all__<\/code> leads to this happening more easily.\nIt is the same reasoning as to why <code>exclude<\/code> shouldn&#x27;t be used on Forms or Serializers.<\/p>\n<p>Secondly, you might think it&#x27;s a handy shortcut since you are just repeating information. However the list of fields in a Form represent a fundamentally different concept in your app. Model fields declare how data should be stored\nin your database, a Form doesn&#x27;t represent this, but what data a user ought to be inputing into a webpage. These are related but not the same. A quick example of this is agreeing to T&amp;C&#x27;s, in the database it is often best to store this\nas a DateTimeField, where as Form would simply require a checkbox. The same would go for fields that get populated on model <code>save<\/code> etc.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-12","title":"Don't use `__all__`","summary":"A best practice with Django Forms","date_modified":"2024-01-23T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#forms","#tips"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-10","content_html":"<p>Well this is post number 10 and I while I have written each day I said I would keeping to time has been difficult and the content ideas still need to flowing before I sit down to type.\nI have even found myself authoring the day&#x27;s message in my head, those days are certainly easier and are normally helped by deciding the next&#x27;s day&#x27;s topic at the end of the current day&#x27;s writing.<\/p>\n<p>I also think these could come together quite nicely into a smallish newsletter? and I think I will publish these to my site publically. Also Monday&#x27;s definitely seem harder after a 2 day break.<\/p>\n<p>Right that\u2019s me done with the 100 words, tomorrow we will back to Django and I am going to start going through my personal set of tips &amp; tricks or common issue I see from the Discord Server.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-10","title":"10 days in a 100 words","summary":"A small retro of the last 10 days","date_modified":"2024-01-22T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#retro"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-11","content_html":"<p>Today, we are going to cover a simple way to customise any template that lives in a third-party package, this includes django itself - most notably the admin &amp; auth modules.<\/p>\n<ol>\n<li>Locate the which template contains the html you need to override and copy the contents and make a note of the path relative the parent <code>templates<\/code> directory. For example to the admin index template is located at <code>django\/contrib\/admin\/templates\/admin\/index.html<\/code> so we need <code>admin\/index.html<\/code><\/li>\n<li>Recreate this file in your project with the exact same path under a templates directory.<\/li>\n<li>Paste in the exist contents of the template.<\/li>\n<li>Make your changes and see them reflected on the relevant page(s) in your browser<\/li>\n<\/ol>\n<p>Done! Tomorrow another quick tip!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-11","title":"Django Template tips","summary":"Some useful django template tips","date_modified":"2024-01-22T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#templates","#tips"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-9","content_html":"<p>Django provides a concept called signals, which allow you as a developer to listen to some predefined events or create your own for other code to the execute.\nAlthough signals feel like an asynchronous mechanism, they are synchronous so they are not the place to do the long running processes, they should still be off loaded to a background worker.<\/p>\n<p>So when should you use signals? For most engineers almost never in a typical project, as they make control flow harder to follow and therefore making maintainablity and readability harder.\nThe time to use them is when you want to explicitly invert the control flow to break a circular dependency with between apps, most typically this is when a reusable app is the desired outcome.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-9","title":"Django Signals in 100 words","summary":"What are signals and when to use them","date_modified":"2024-01-19T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#signals"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-8","content_html":"<p>Django Forms primarily perform two functions within a Django application. They can render html fields and related tags like labels and legends and they can also validate and process data submitted to a view.\nIn terms of a codebase they typically live in their own file, <code>forms.py<\/code>.<\/p>\n<p>Forms are the most common entry point to business logic which is normally going to be saving some processed data to the database. This keeps what is happening from a view to a form to one or more models clear and explicit, which\nleads to easier readability and maintainablity. Essentially your future self or teammates will thank you.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-8","title":"Django Forms in 100 words","summary":"The basics of what forms provide","date_modified":"2024-01-18T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#forms"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-7","content_html":"<p>Our final relationship type is many to many relationships, these are the most complex, yet flexible, type of relationship, since they require an intermediate database table, known as a through table.\nThis table can track other data but by default it just two Foreign Keys to each side of the relationship.<\/p>\n<p>Examples of a many to many include authors of books (some books do have multiple authors!), items in a supermarket or ingredients for a meal. To go back to our English sentence it is as follows:<\/p>\n<blockquote>\n<p>&quot;A Meal can be made of many ingredients, and an ingredient can be part of many meals&quot;<\/p>\n<\/blockquote>\n<p>Django does make this easy with the <code>ManyToManyField<\/code> which by default handles the creation of the through table, although you can specify one if you need, and the ORM has a nice API to jump over the through table to the other side of the relationship.<\/p>\n<p>Tomorrow, we move on to forms!<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-7","title":"Database relationships in Django: Part 3","summary":"Day 7 wraps up with many to many","date_modified":"2024-01-17T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#models","#relations"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-6","content_html":"<p>Today is all about Foreign Keys, which are the most common relational type that exists in an application. Often referred to as a One to Many relationship, where a single object can have many related items.\nExamples include a shopping cart having many items, a customer borrowing many books from a library or a user organising many events. In each case the following English statement is true:<\/p>\n<blockquote>\n<p>&quot;A Customer can borrow many books, but a book can only be borrowed by a single customer&quot;<\/p>\n<\/blockquote>\n<p>Again this simple sentence ought to convey the constraints on this relationship. This allows a single object to be referenced by multiple rows in another table with the reference being the primary key of the single object.<\/p>\n<p>Tomorrow we will cover the final and most complex relation type Many to Many relationships<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-6","title":"Database relationships in Django: Part 2","summary":"Day 6 continues updating a relationships to cover Foreign Keys","date_modified":"2024-01-16T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#models"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-5","content_html":"<p>I have procrastinated on this today, it&#x27;s almost 10am and I was supposed to write this almost an hour ago. Next on the list of Django components to understand is database relationships.\nReally this isn&#x27;t specific to Django at all, it&#x27;s about data modelling and database design. There are 3 types of relationship in a relational database: a Foreign Key, One to One and Many to Many.\nDjango provides fields for all these out of the box.<\/p>\n<p>I find the easiest way to think about how they work with plain English (or language of your choosing!). Today let&#x27;s start with the simplest One to One. The most common use case for this is a User Profile table\nbeing linked to a User table. The English I think about goes like this:<\/p>\n<blockquote>\n<p>&quot;A User can only have a single Profile, and a Profile can only belong to a single User&quot;.<\/p>\n<\/blockquote>\n<p>Reading that should reveal what a One to One is all about, allowing a single row to be linked to only another single row and not multiple rows. It is the most restrictive out of the three relationship types and generally the least used in my experience.<\/p>\n<p>Tomorrow we will talk about the most common, Foreign Keys.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-5","title":"Database relationships in Django","summary":"Day 5 is all about making relationships in Django, it can be complicated.","date_modified":"2024-01-15T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#models"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-4","content_html":"<p>Pretty much all interesting websites store and process data of some kind, by this I mean these using a database.\nDjango has native support for the main popular relation databases through Models, migrations as the Object Relation Mapper (ORM).<\/p>\n<p>What do those three components give you? A lot! Let&#x27;s start with Models; these essentially represent database tables as a Python class fields are defined as class attributes which is where data is stored.\nNext there is migrations, with two simple commands Django will compute the difference between your code and the database to generate the commands required to migrate the database and of course apply those changes.\nFinally, there is the ORM, this allows a developer to query a Model and return data to the browser with ease or create new objects from the client.<\/p>\n<p>Now we have the components of a fully functional website.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-4","title":"The core building blocks of Django: Models","summary":"Day 4: Intro to Django models","date_modified":"2024-01-12T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django","#models"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-3","content_html":"<p>Yesterday we description at a high level what Django is. Today, we&#x27;re going to briefly walk through the core components that will exist is every Django application.<\/p>\n<p>First up is a list of URL patterns. These can be static, for example <code>\/about<\/code> or have dynamic aspects such as the username for a profile page <code>\/profile\/&lt;username&gt;<\/code>. Django has these in a list and will match against the first pattern.\nThis is often a common gotcha for beginners as they create URLs in the wrong order so execute the unexpected code.<\/p>\n<p>URLs are linked to the second component, Views. This is the main place where you write business logic, at least to begin with. A View takes a request and arguments from the URL and needs to return a response.\nThe sky&#x27;s the limit in views you can do what you like (more on this another day).<\/p>\n<p>Finally, there are templates. While strictly not needed, templates make it easy to write the actual HTML that is returned to the client and sprinkle in the data collected from the view. Templates are rendered on the server and sent back to the browser to display.<\/p>\n<p>With those 3 components you can build simple Django websites, however it gets more interesting when you can store and query data from database, which will be tomorrow&#x27;s post.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-3","title":"The core building blocks of Django","summary":"Day 3: What are the core components of Django?","date_modified":"2024-01-11T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-2","content_html":"<p>Simply put Django is a framework for building web applications and websites. But what does that actually mean (without going into technical jargon)?<\/p>\n<p>Django provides a set of tooling to easily accomplish the tasks required to create different webpages populated by data stored somewhere else (typically a database) or have pages that can take data from the browser and store it in a database.<\/p>\n<p>Django is designed to be &#x27;batteries included&#x27;, which means common tasks such as allowing user sign up are easy to setup and require little work by us as software developers. Finally and perhaps the best thing is Django is completely run\nby the community, this means people volunteer their own time to make improvements and help others; it also means that a single company is not able to change the direction of Django meaning it has and will be around for the long haul.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-2","title":"Django in a 100 Words","summary":"Day 2: Django in a approximately 100 words","date_modified":"2024-01-10T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#django"]},{"id":"https:\/\/softwarecrafts.co.uk\/100-words\/day-1","content_html":"<p>I suppose I will start with a bit of a meta post (is post even the correct noun?), anyway I am starting small and consistently having read the short read &quot;100 word writing habit&quot; by David Kadavy.\nMy goal is to write each weekday before doing any work. I think my focus will be on the subject of mostly Django &amp; Python. &quot;X&quot; in a 100 words has a nice ring to it, starting broad by eventually covering most of the framework and beyond!\nI will try not to edit myself and will hopefully publish a lot (if not all) of these to my website! And I am done, that last sentence pushed me over 100 words, see you tomorrow.<\/p>","url":"https:\/\/softwarecrafts.co.uk\/100-words\/day-1","title":"100 Word writing habit","summary":"The first day of starting a writing habit","date_modified":"2024-01-09T00:00:00.000Z","author":{"name":"Software Crafts"},"tags":["#100_words","#habit"]}]}