Add dietary property fields (is_vegan, is_vegetarian) to Ingredient#2218
Add dietary property fields (is_vegan, is_vegetarian) to Ingredient#2218rolandgeider merged 11 commits into
Conversation
Extract dietary classification from Open Food Facts ingredients_analysis_tags during import and expose via API. - Add nullable boolean fields is_vegan and is_vegetarian to Ingredient - Parse en:vegan, en:non-vegan, en:vegetarian, en:non-vegetarian tags from OFF product data (unknown/maybe states map to NULL) - Include fields in API serializers (ingredient and ingredientinfo) - Add filterset support for exact match filtering (?is_vegan=true) - Handle fields in wger API sync (extract_info_from_wger_api) - Add migration 0028_ingredient_dietary_properties - Add tests for all OFF dietary tag parsing scenarios Refs #394
| is_vegetarian = None | ||
| analysis_tags = product_data.get('ingredients_analysis_tags', []) | ||
| for tag in analysis_tags: | ||
| if tag == 'en:vegan': |
There was a problem hiding this comment.
do the tags have these names for all ingredients?
There was a problem hiding this comment.
The tags use Open Food Facts' standardized ingredients_analysis_tags field which applies consistent tag names across all ingredients that have been analyzed. However, not all ingredients have been analyzed — for those, the field is absent or null, which is why the model fields are nullable. The tags follow OFF's fixed vocabulary: en:vegan, en:non-vegan, en:maybe-vegan, en:vegan-status-unknown, en:vegetarian, en:non-vegetarian, en:maybe-vegetarian, en:vegetarian-status-unknown. So: yes, the tags are consistent for ingredients that have them, but coverage is not 100%.
|
Hey @rolandgeider — yes, these are the standardized tag values from Open Food Facts'
The To your review comment specifically: these aren't free-form tags that vary per ingredient. They're from a controlled vocabulary that OFF applies uniformly. The PR maps You can verify the taxonomy here: https://world.openfoodfacts.org/ingredients-analysis Let me know if you'd want the extraction logic handled differently. |
|
thanks, good to know! There are a couple of things that would be nice to extract as well and last time I was looking at their dataset found their data model somewhat confusing. BTW, do you want to give the UI a shot as well or should I take a look at that? |
|
@rolandgeider Yeah I'd be happy to take a shot at the UI — makes sense to wire it up while the model changes are fresh. I'll add filter controls to the ingredient list and a display in the detail view. If there are specific UI patterns or component conventions you prefer in the wger frontend, just point me at an example and I'll follow that. And yeah, OFF's tag system is... characterful. Happy to help extract more fields if you can point me at which ones would be most useful. |
- Ingredient detail view: show a "Dietary information" table with green/red badges for is_vegan and is_vegetarian when the data is present (nullable fields are hidden when null). - Ingredient list view: display vegan/vegetarian badges next to each ingredient name in the list, and add sidebar filter checkboxes so users can narrow the list to vegan-only or vegetarian-only items. - IngredientListView.get_queryset: apply is_vegan=True / is_vegetarian=True DB filters when the corresponding GET params are present; also bypass the page-level cache for filtered requests so results stay accurate. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
😆 We were looking if they had something like a "serving size", also extracting the categories would be nice (of course both of these would need other backend changes) |
|
Happy to move forward with the UI once we nail down conventions. A couple of questions to make sure I get it right:
Also re: serving size and categories — those sound like separate PRs given the additional backend changes they'd need. Happy to tackle them as follow-ups once this one is in. |
|
Went ahead and investigated the autocompleter infrastructure — the search flow uses Opened a companion PR against the React app with the frontend changes: wger-project/react#1197 It adds "Vegan only" and "Vegetarian only" toggle switches to |
The search can now be done more cleanly via the regular "ingredientinfo" endpoint so this is not needed anymore
This is mostly for completeness, these forms are rarely used since most ingredients are just imported from OFF
This is cleaner and removes boilerplate
|
Branch updated to current with upstream master (was 17 commits behind). Merge state is now clean. Re CI: shows action_required — GitHub first-time contributor fork restriction, needs a maintainer to approve the workflow run. |
|
finally, everything is green! I'll merge this and the react pr later today and run a full re-import of OFF the next week so that populate the fields in the db |

Summary
Adds
is_veganandis_vegetariannullable boolean fields to theIngredientmodel, populated from Open Food Factsingredients_analysis_tagsduring import.BooleanField(null=True)fields onIngredient—is_veganandis_vegetarianen:vegan,en:non-vegan,en:vegetarian,en:non-vegetariantags; unknown/maybe states map toNULLIngredientSerializerandIngredientInfoSerializer, filterable via?is_vegan=true/?is_vegetarian=trueextract_info_from_wger_apipasses through these fields during instance-to-instance sync0028_ingredient_dietary_properties— backward-compatible, all existing rows default toNULLContext
Open Food Facts already provides ingredient analysis data that classifies products as vegan/vegetarian/non-vegan/non-vegetarian. This PR captures that data during import so it can be used for filtering. This addresses the long-standing request in #394 for dietary categorization of ingredients.
The three-state approach (
True/False/NULL) correctly handles the reality that many products have unknown dietary status —NULLmeans "not determined" rather than forcing a potentially incorrect classification.Test plan
test_off.pytests pass with updated fixturestest_ingredient.pyfetch-from-OFF test verifies fields propagate to saved modelNULLdefaults)GET /api/v2/ingredient/?is_vegan=true